diff --git a/FSharp.Data.GraphQL.sln b/FSharp.Data.GraphQL.sln index f08c5fa9d..f77df7cda 100644 --- a/FSharp.Data.GraphQL.sln +++ b/FSharp.Data.GraphQL.sln @@ -137,6 +137,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "server", "server", "{9D5C46 samples\client-provider\file-upload\server\types.mjs = samples\client-provider\file-upload\server\types.mjs EndProjectSection EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Data.GraphQL.Server.AspNet", "src\FSharp.Data.GraphQL.Server.AspNet\FSharp.Data.GraphQL.Server.AspNet.fsproj", "{F5640838-93AB-4903-822B-D31D4D0FEBE2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -243,6 +245,18 @@ Global {B837B3ED-83CE-446F-A4E5-44CB06AA6505}.Release|x64.Build.0 = Release|Any CPU {B837B3ED-83CE-446F-A4E5-44CB06AA6505}.Release|x86.ActiveCfg = Release|Any CPU {B837B3ED-83CE-446F-A4E5-44CB06AA6505}.Release|x86.Build.0 = Release|Any CPU + {F5640838-93AB-4903-822B-D31D4D0FEBE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5640838-93AB-4903-822B-D31D4D0FEBE2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5640838-93AB-4903-822B-D31D4D0FEBE2}.Debug|x64.ActiveCfg = Debug|Any CPU + {F5640838-93AB-4903-822B-D31D4D0FEBE2}.Debug|x64.Build.0 = Debug|Any CPU + {F5640838-93AB-4903-822B-D31D4D0FEBE2}.Debug|x86.ActiveCfg = Debug|Any CPU + {F5640838-93AB-4903-822B-D31D4D0FEBE2}.Debug|x86.Build.0 = Debug|Any CPU + {F5640838-93AB-4903-822B-D31D4D0FEBE2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5640838-93AB-4903-822B-D31D4D0FEBE2}.Release|Any CPU.Build.0 = Release|Any CPU + {F5640838-93AB-4903-822B-D31D4D0FEBE2}.Release|x64.ActiveCfg = Release|Any CPU + {F5640838-93AB-4903-822B-D31D4D0FEBE2}.Release|x64.Build.0 = Release|Any CPU + {F5640838-93AB-4903-822B-D31D4D0FEBE2}.Release|x86.ActiveCfg = Release|Any CPU + {F5640838-93AB-4903-822B-D31D4D0FEBE2}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -268,6 +282,7 @@ Global {B837B3ED-83CE-446F-A4E5-44CB06AA6505} = {F66BEE6C-0CB7-4F39-97E4-243F797E8723} {A47968E2-CDD1-4BCF-9093-D0C5225A815B} = {3D948D55-3CD2-496D-A04C-3B4E7BB69140} {9D5C46E8-0C07-4384-8E58-903F7C2C7171} = {A47968E2-CDD1-4BCF-9093-D0C5225A815B} + {F5640838-93AB-4903-822B-D31D4D0FEBE2} = {BEFD8748-2467-45F9-A4AD-B450B12D5F78} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C5B9895C-9DF8-4557-8D44-7D0C4C31F86E} diff --git a/build.fsx b/build.fsx index abe91478c..15ca3a5a4 100644 --- a/build.fsx +++ b/build.fsx @@ -253,6 +253,9 @@ Target.create "PackMiddleware" (fun _ -> pack "Server.Middleware" ) +Target.create "PackAspNet" (fun _ -> + pack "Server.AspNet" +) // -------------------------------------------------------------------------------------- // Run all targets by default. Invoke 'build -t ' to override @@ -278,6 +281,7 @@ Target.create "PackAll" ignore ==> "PackServer" ==> "PackClient" ==> "PackMiddleware" + ==> "PackAspNet" ==> "PackAll" Target.runOrDefault "All" \ No newline at end of file diff --git a/build.fsx.lock b/build.fsx.lock index 0981d7d53..918bd502f 100644 --- a/build.fsx.lock +++ b/build.fsx.lock @@ -189,27 +189,27 @@ NUGET System.Security.AccessControl (>= 5.0) System.Security.Principal.Windows (>= 5.0) Mono.Posix.NETStandard (1.0) - MSBuild.StructuredLogger (2.1.404) + MSBuild.StructuredLogger (2.1.488) Microsoft.Build (>= 16.4) Microsoft.Build.Framework (>= 16.4) Microsoft.Build.Tasks.Core (>= 16.4) Microsoft.Build.Utilities.Core (>= 16.4) Newtonsoft.Json (13.0.1) - NuGet.Common (5.9) - NuGet.Frameworks (>= 5.9) - NuGet.Configuration (5.9) - NuGet.Common (>= 5.9) + NuGet.Common (5.9.1) + NuGet.Frameworks (>= 5.9.1) + NuGet.Configuration (5.9.1) + NuGet.Common (>= 5.9.1) System.Security.Cryptography.ProtectedData (>= 4.4) - NuGet.Frameworks (5.9) - NuGet.Packaging (5.9) + NuGet.Frameworks (5.9.1) + NuGet.Packaging (5.9.1) Newtonsoft.Json (>= 9.0.1) - NuGet.Configuration (>= 5.9) - NuGet.Versioning (>= 5.9) + NuGet.Configuration (>= 5.9.1) + NuGet.Versioning (>= 5.9.1) System.Security.Cryptography.Cng (>= 5.0) System.Security.Cryptography.Pkcs (>= 5.0) - NuGet.Protocol (5.9) - NuGet.Packaging (>= 5.9) - NuGet.Versioning (5.9) + NuGet.Protocol (5.9.1) + NuGet.Packaging (>= 5.9.1) + NuGet.Versioning (5.9.1) Octokit (0.50) runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) runtime.debian.9-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) diff --git a/paket.dependencies b/paket.dependencies index 092d9ae36..e80229c81 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -6,6 +6,7 @@ group Common source https://api.nuget.org/v3/index.json frameworks: net5.0, netstandard2.0 + nuget System.Text.Json nuget System.Reactive # Be explicit about FSharp.Core 4.7.2 when designing libraries. @@ -19,8 +20,8 @@ group Common nuget Microsoft.Extensions.Http 5 # Those are needed for the client type provider. - github fsprojects/FSharp.TypeProviders.SDK:377d56321ad062985ed5aa19f205c1c4f04ef328 src/ProvidedTypes.fsi - github fsprojects/FSharp.TypeProviders.SDK:377d56321ad062985ed5aa19f205c1c4f04ef328 src/ProvidedTypes.fs + github fsprojects/FSharp.TypeProviders.SDK:f4aca36af04aa84b16ec04df6f6bf55ac2f17a73 src/ProvidedTypes.fsi + github fsprojects/FSharp.TypeProviders.SDK:f4aca36af04aa84b16ec04df6f6bf55ac2f17a73 src/ProvidedTypes.fs group TestsAndSamples source https://api.nuget.org/v3/index.json @@ -38,4 +39,14 @@ group TestsAndSamples nuget xunit.runner.console nuget xunit.runner.visualstudio nuget BenchmarkDotNet - nuget FSharp.Data.TypeProviders \ No newline at end of file + nuget FSharp.Data.TypeProviders + +group AspNetLib + source https://api.nuget.org/v3/index.json + frameworks: net5.0, netstandard2.0 + + nuget Microsoft.AspNetCore.Http + nuget Microsoft.AspNetCore.Http.Abstractions + nuget Microsoft.AspNetCore.Http.Extensions + nuget System.Text.Json + nuget System.IO.Pipelines diff --git a/paket.lock b/paket.lock index 86700f6ee..23b8a611d 100644 --- a/paket.lock +++ b/paket.lock @@ -1,6 +1,71 @@ NUGET remote: https://api.nuget.org/v3/index.json - NuGet.CommandLine (5.8.1) + NuGet.CommandLine (5.10) + +GROUP AspNetLib +RESTRICTION: || (== net50) (== netstandard2.0) +NUGET + remote: https://api.nuget.org/v3/index.json + Microsoft.AspNetCore.Http (2.2.2) + Microsoft.AspNetCore.Http.Abstractions (>= 2.2) + Microsoft.AspNetCore.WebUtilities (>= 2.2) + Microsoft.Extensions.ObjectPool (>= 2.2) + Microsoft.Extensions.Options (>= 2.2) + Microsoft.Net.Http.Headers (>= 2.2) + Microsoft.AspNetCore.Http.Abstractions (2.2) + Microsoft.AspNetCore.Http.Features (>= 2.2) + System.Text.Encodings.Web (>= 4.5) + Microsoft.AspNetCore.Http.Extensions (2.2) + Microsoft.AspNetCore.Http.Abstractions (>= 2.2) + Microsoft.Extensions.FileProviders.Abstractions (>= 2.2) + Microsoft.Net.Http.Headers (>= 2.2) + System.Buffers (>= 4.5) + Microsoft.AspNetCore.Http.Features (5.0.9) + Microsoft.Extensions.Primitives (>= 5.0.1) + System.IO.Pipelines (>= 5.0.1) + Microsoft.AspNetCore.WebUtilities (2.2) + Microsoft.Net.Http.Headers (>= 2.2) + System.Text.Encodings.Web (>= 4.5) + Microsoft.Bcl.AsyncInterfaces (5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (== netstandard2.0) + Microsoft.Extensions.DependencyInjection.Abstractions (5.0) + Microsoft.Extensions.FileProviders.Abstractions (5.0) + Microsoft.Extensions.Primitives (>= 5.0) + Microsoft.Extensions.ObjectPool (5.0.9) + Microsoft.Extensions.Options (5.0) + Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) + Microsoft.Extensions.Primitives (>= 5.0) + Microsoft.Extensions.Primitives (5.0.1) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (== netstandard2.0) + System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Runtime.CompilerServices.Unsafe (>= 5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + Microsoft.Net.Http.Headers (2.2.8) + Microsoft.Extensions.Primitives (>= 2.2) + System.Buffers (>= 4.5) + System.Buffers (4.5.1) + System.IO.Pipelines (5.0.1) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= xamarinios)) (&& (== net50) (>= xamarinmac)) (&& (== net50) (>= xamarintvos)) (&& (== net50) (>= xamarinwatchos)) (== netstandard2.0) + System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Memory (4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netstandard1.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= xamarinios)) (&& (== net50) (>= xamarinmac)) (&& (== net50) (>= xamarintvos)) (&& (== net50) (>= xamarinwatchos)) (== netstandard2.0) + System.Numerics.Vectors (>= 4.4) - restriction: || (&& (== net50) (< netcoreapp2.0)) (== netstandard2.0) + System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard1.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (&& (== net50) (>= xamarinios)) (&& (== net50) (>= xamarinmac)) (&& (== net50) (>= xamarintvos)) (&& (== net50) (>= xamarinwatchos)) (== netstandard2.0) + System.Numerics.Vectors (4.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (== netstandard2.0) + System.Runtime.CompilerServices.Unsafe (5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Text.Encodings.Web (5.0.1) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (== netstandard2.0) + System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Text.Json (5.0.2) + Microsoft.Bcl.AsyncInterfaces (>= 5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (== netstandard2.0) + System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Numerics.Vectors (>= 4.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (== netstandard2.0) + System.Runtime.CompilerServices.Unsafe (>= 5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Text.Encodings.Web (>= 5.0.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Threading.Tasks.Extensions (4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard1.0)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= wp8)) (== netstandard2.0) GROUP Common RESTRICTION: || (== net50) (== netstandard2.0) @@ -9,9 +74,9 @@ NUGET FParsec (1.1.1) FSharp.Core (>= 4.3.4) FSharp.Core (4.7.2) - Microsoft.Bcl.AsyncInterfaces (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netstandard2.1)) (== netstandard2.0) + Microsoft.Bcl.AsyncInterfaces (5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (== netstandard2.0) - Microsoft.Extensions.DependencyInjection (5.0.1) + Microsoft.Extensions.DependencyInjection (5.0.2) Microsoft.Bcl.AsyncInterfaces (>= 5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netstandard2.1)) (== netstandard2.0) Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (== netstandard2.0) @@ -41,38 +106,49 @@ NUGET System.Diagnostics.DiagnosticSource (5.0.1) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netstandard2.1)) (== netstandard2.0) System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net45) (< netstandard1.3)) (&& (== net50) (>= net46)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) System.Runtime.CompilerServices.Unsafe (>= 5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net45) (< netstandard1.3)) (&& (== net50) (>= net46)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Memory (4.5.4) - restriction: || (&& (== net50) (>= net45) (< netstandard1.3)) (&& (== net50) (>= net46)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Memory (4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netstandard1.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= xamarinios)) (&& (== net50) (>= xamarinmac)) (&& (== net50) (>= xamarintvos)) (&& (== net50) (>= xamarinwatchos)) (== netstandard2.0) System.Numerics.Vectors (>= 4.4) - restriction: || (&& (== net50) (< netcoreapp2.0)) (== netstandard2.0) System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard1.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (&& (== net50) (>= xamarinios)) (&& (== net50) (>= xamarinmac)) (&& (== net50) (>= xamarintvos)) (&& (== net50) (>= xamarinwatchos)) (== netstandard2.0) - System.Numerics.Vectors (4.5) - restriction: || (&& (== net50) (>= net45) (< netstandard1.3)) (&& (== net50) (>= net46)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Numerics.Vectors (4.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (== netstandard2.0) System.Reactive (5.0) System.Runtime.InteropServices.WindowsRuntime (>= 4.3) - restriction: || (&& (== net50) (< netcoreapp3.1)) (== netstandard2.0) System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net472)) (&& (== net50) (< netcoreapp3.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) System.Runtime (4.3.1) - restriction: || (&& (== net50) (< netcoreapp3.1)) (== netstandard2.0) Microsoft.NETCore.Platforms (>= 1.1.1) Microsoft.NETCore.Targets (>= 1.1.3) - System.Runtime.CompilerServices.Unsafe (5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net45) (< netstandard1.3)) (&& (== net50) (>= net46)) (&& (== net50) (>= net461)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (< netstandard2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Runtime.CompilerServices.Unsafe (5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) System.Runtime.InteropServices.WindowsRuntime (4.3) - restriction: || (&& (== net50) (< netcoreapp3.1)) (== netstandard2.0) System.Runtime (>= 4.3) - System.Threading.Tasks.Extensions (4.5.4) - restriction: || (&& (== net50) (>= net472)) (&& (== net50) (< netcoreapp3.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Text.Encodings.Web (5.0.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (== netstandard2.0) + System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Text.Json (5.0.2) + Microsoft.Bcl.AsyncInterfaces (>= 5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (== netstandard2.0) + System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Numerics.Vectors (>= 4.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (== netstandard2.0) + System.Runtime.CompilerServices.Unsafe (>= 5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Text.Encodings.Web (>= 5.0.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Threading.Tasks.Extensions (4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard1.0)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= wp8)) (== netstandard2.0) GITHUB remote: fsprojects/FSharp.TypeProviders.SDK - src/ProvidedTypes.fs (377d56321ad062985ed5aa19f205c1c4f04ef328) - src/ProvidedTypes.fsi (377d56321ad062985ed5aa19f205c1c4f04ef328) + src/ProvidedTypes.fs (f4aca36af04aa84b16ec04df6f6bf55ac2f17a73) + src/ProvidedTypes.fsi (f4aca36af04aa84b16ec04df6f6bf55ac2f17a73) GROUP TestsAndSamples RESTRICTION: || (== net50) (== netstandard2.0) NUGET remote: https://api.nuget.org/v3/index.json - BenchmarkDotNet (0.12.1) - BenchmarkDotNet.Annotations (>= 0.12.1) + BenchmarkDotNet (0.13.1) + BenchmarkDotNet.Annotations (>= 0.13.1) CommandLineParser (>= 2.4.3) - Iced (>= 1.4) + Iced (>= 1.8) Microsoft.CodeAnalysis.CSharp (>= 2.10) Microsoft.Diagnostics.NETCore.Client (>= 0.2.61701) - Microsoft.Diagnostics.Runtime (>= 1.1.57604) - Microsoft.Diagnostics.Tracing.TraceEvent (>= 2.0.49) + Microsoft.Diagnostics.Runtime (>= 1.1.126102) + Microsoft.Diagnostics.Tracing.TraceEvent (>= 2.0.61) Microsoft.DotNet.PlatformAbstractions (>= 2.1) Microsoft.Win32.Registry (>= 4.5) Perfolizer (>= 0.2.1) @@ -81,156 +157,54 @@ NUGET System.Reflection.Emit.Lightweight (>= 4.3) System.Threading.Tasks.Extensions (>= 4.5.2) System.ValueTuple (>= 4.5) - BenchmarkDotNet.Annotations (0.12.1) + BenchmarkDotNet.Annotations (0.13.1) CommandLineParser (2.8) - FSharp.Core (5.0.1) + FSharp.Core (5.0.2) FSharp.Data.TypeProviders (5.0.0.6) FSharp.Core (>= 3.1.2.5) - Giraffe (4.1) - FSharp.Core (>= 4.7) - Microsoft.AspNetCore.Authentication (>= 2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Authorization (>= 2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Diagnostics (>= 2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Hosting.Abstractions (>= 2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.ResponseCaching (>= 2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.IO.RecyclableMemoryStream (>= 1.2.2) - Newtonsoft.Json (>= 12.0.2) - TaskBuilder.fs (>= 2.1) - Utf8Json (>= 1.3.7) - Iced (1.11) - Microsoft.AspNetCore.Authentication (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Authentication.Core (>= 2.2) - Microsoft.AspNetCore.DataProtection (>= 2.2) - Microsoft.AspNetCore.Http (>= 2.2) - Microsoft.AspNetCore.Http.Extensions (>= 2.2) - Microsoft.Extensions.Logging.Abstractions (>= 2.2) - Microsoft.Extensions.Options (>= 2.2) - Microsoft.Extensions.WebEncoders (>= 2.2) - Microsoft.AspNetCore.Authentication.Abstractions (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Http.Abstractions (>= 2.2) - Microsoft.Extensions.Logging.Abstractions (>= 2.2) - Microsoft.Extensions.Options (>= 2.2) - Microsoft.AspNetCore.Authentication.Core (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Authentication.Abstractions (>= 2.2) - Microsoft.AspNetCore.Http (>= 2.2) - Microsoft.AspNetCore.Http.Extensions (>= 2.2) - Microsoft.AspNetCore.Authorization (5.0.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Metadata (>= 5.0.5) - Microsoft.Extensions.Logging.Abstractions (>= 5.0) - Microsoft.Extensions.Options (>= 5.0) - Microsoft.AspNetCore.Cryptography.Internal (5.0.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.DataProtection (5.0.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Cryptography.Internal (>= 5.0.5) - Microsoft.AspNetCore.DataProtection.Abstractions (>= 5.0.5) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) - Microsoft.Extensions.Hosting.Abstractions (>= 5.0) - Microsoft.Extensions.Logging.Abstractions (>= 5.0) - Microsoft.Extensions.Options (>= 5.0) - Microsoft.Win32.Registry (>= 5.0) - System.Security.Cryptography.Xml (>= 5.0) - System.Security.Principal.Windows (>= 5.0) - restriction: == netstandard2.0 - Microsoft.AspNetCore.DataProtection.Abstractions (5.0.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Diagnostics (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Diagnostics.Abstractions (>= 2.2) - Microsoft.AspNetCore.Hosting.Abstractions (>= 2.2) - Microsoft.AspNetCore.Http.Extensions (>= 2.2) - Microsoft.AspNetCore.WebUtilities (>= 2.2) - Microsoft.Extensions.FileProviders.Physical (>= 2.2) - Microsoft.Extensions.Logging.Abstractions (>= 2.2) - Microsoft.Extensions.Options (>= 2.2) - System.Diagnostics.DiagnosticSource (>= 4.5) - System.Reflection.Metadata (>= 1.6) - Microsoft.AspNetCore.Diagnostics.Abstractions (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Hosting.Abstractions (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Hosting.Server.Abstractions (>= 2.2) - Microsoft.AspNetCore.Http.Abstractions (>= 2.2) - Microsoft.Extensions.Hosting.Abstractions (>= 2.2) - Microsoft.AspNetCore.Hosting.Server.Abstractions (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Http.Features (>= 2.2) - Microsoft.Extensions.Configuration.Abstractions (>= 2.2) - Microsoft.AspNetCore.Http (2.2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Http.Abstractions (>= 2.2) - Microsoft.AspNetCore.WebUtilities (>= 2.2) - Microsoft.Extensions.ObjectPool (>= 2.2) - Microsoft.Extensions.Options (>= 2.2) - Microsoft.Net.Http.Headers (>= 2.2) - Microsoft.AspNetCore.Http.Abstractions (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Http.Features (>= 2.2) - System.Text.Encodings.Web (>= 4.5) - Microsoft.AspNetCore.Http.Extensions (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Http.Abstractions (>= 2.2) - Microsoft.Extensions.FileProviders.Abstractions (>= 2.2) - Microsoft.Net.Http.Headers (>= 2.2) - System.Buffers (>= 4.5) - Microsoft.AspNetCore.Http.Features (5.0.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.Primitives (>= 5.0.1) - System.IO.Pipelines (>= 5.0.1) - Microsoft.AspNetCore.Metadata (5.0.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.ResponseCaching (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.AspNetCore.Http (>= 2.2) - Microsoft.AspNetCore.Http.Extensions (>= 2.2) - Microsoft.AspNetCore.ResponseCaching.Abstractions (>= 2.2) - Microsoft.Extensions.Caching.Memory (>= 2.2) - Microsoft.Extensions.Logging.Abstractions (>= 2.2) - Microsoft.AspNetCore.ResponseCaching.Abstractions (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.Primitives (>= 2.2) - Microsoft.AspNetCore.WebUtilities (2.2) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Net.Http.Headers (>= 2.2) - System.Text.Encodings.Web (>= 4.5) + Giraffe (5.0) + FSharp.Core (>= 5.0) - restriction: || (== net50) (&& (== netstandard2.0) (>= net50)) + Giraffe.ViewEngine (>= 1.3) - restriction: || (== net50) (&& (== netstandard2.0) (>= net50)) + Microsoft.IO.RecyclableMemoryStream (>= 1.3.6) - restriction: || (== net50) (&& (== netstandard2.0) (>= net50)) + Newtonsoft.Json (>= 12.0.3) - restriction: || (== net50) (&& (== netstandard2.0) (>= net50)) + Ply (>= 0.3.1) - restriction: || (== net50) (&& (== netstandard2.0) (>= net50)) + System.Text.Json (>= 5.0.2) - restriction: || (== net50) (&& (== netstandard2.0) (>= net50)) + Utf8Json (>= 1.3.7) - restriction: || (== net50) (&& (== netstandard2.0) (>= net50)) + Giraffe.ViewEngine (1.4) - restriction: || (== net50) (&& (== netstandard2.0) (>= net50)) + FSharp.Core (>= 5.0) + Iced (1.14) Microsoft.Bcl.AsyncInterfaces (5.0) System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (== netstandard2.0) Microsoft.CodeAnalysis.Analyzers (3.3.2) - Microsoft.CodeAnalysis.Common (3.9) - Microsoft.CodeAnalysis.Analyzers (>= 3.0) + Microsoft.CodeAnalysis.Common (3.11) + Microsoft.CodeAnalysis.Analyzers (>= 3.3.2) System.Collections.Immutable (>= 5.0) System.Memory (>= 4.5.4) System.Reflection.Metadata (>= 5.0) System.Runtime.CompilerServices.Unsafe (>= 5.0) System.Text.Encoding.CodePages (>= 4.5.1) System.Threading.Tasks.Extensions (>= 4.5.4) - Microsoft.CodeAnalysis.CSharp (3.9) - Microsoft.CodeAnalysis.Common (3.9) - Microsoft.CodeCoverage (16.9.4) - restriction: || (== net50) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp1.0)) - Microsoft.Diagnostics.NETCore.Client (0.2.217401) + Microsoft.CodeAnalysis.CSharp (3.11) + Microsoft.CodeAnalysis.Common (3.11) + Microsoft.CodeCoverage (16.11) - restriction: || (== net50) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp1.0)) + Microsoft.Diagnostics.NETCore.Client (0.2.236902) Microsoft.Bcl.AsyncInterfaces (>= 1.1) - Microsoft.Diagnostics.Runtime (2.0.217201) - Microsoft.Diagnostics.NETCore.Client (>= 0.2.61701) + Microsoft.Extensions.Logging (>= 2.1.1) + Microsoft.Diagnostics.Runtime (2.0.226801) + Microsoft.Diagnostics.NETCore.Client (>= 0.2.221401) System.Buffers (>= 4.5.1) - System.Collections.Immutable (>= 1.7.1) + System.Collections.Immutable (>= 5.0) System.Memory (>= 4.5.4) - System.Reflection.Metadata (>= 1.8.1) - System.Runtime.CompilerServices.Unsafe (>= 4.7.1) - Microsoft.Diagnostics.Tracing.TraceEvent (2.0.66) + System.Reflection.Metadata (>= 5.0) + System.Runtime.CompilerServices.Unsafe (>= 4.5.3) + Microsoft.Diagnostics.Tracing.TraceEvent (2.0.71) System.Runtime.CompilerServices.Unsafe (>= 4.5.2) Microsoft.DotNet.PlatformAbstractions (3.1.6) - Microsoft.Extensions.Caching.Abstractions (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.Primitives (>= 5.0) - Microsoft.Extensions.Caching.Memory (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.Caching.Abstractions (>= 5.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) - Microsoft.Extensions.Logging.Abstractions (>= 5.0) - Microsoft.Extensions.Options (>= 5.0) - Microsoft.Extensions.Primitives (>= 5.0) - Microsoft.Extensions.Configuration.Abstractions (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.1)) (== netstandard2.0) - Microsoft.Extensions.Primitives (>= 5.0) - Microsoft.Extensions.DependencyInjection (5.0.1) + Microsoft.Extensions.DependencyInjection (5.0.2) Microsoft.Bcl.AsyncInterfaces (>= 5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netstandard2.1)) (== netstandard2.0) Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (== netstandard2.0) Microsoft.Extensions.DependencyInjection.Abstractions (5.0) - Microsoft.Extensions.FileProviders.Abstractions (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.Primitives (>= 5.0) - Microsoft.Extensions.FileProviders.Physical (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.FileProviders.Abstractions (>= 5.0) - Microsoft.Extensions.FileSystemGlobbing (>= 5.0) - Microsoft.Extensions.Primitives (>= 5.0) - Microsoft.Extensions.FileSystemGlobbing (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.Hosting.Abstractions (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Bcl.AsyncInterfaces (>= 5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netstandard2.1)) (== netstandard2.0) - Microsoft.Extensions.Configuration.Abstractions (>= 5.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) - Microsoft.Extensions.FileProviders.Abstractions (>= 5.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (== netstandard2.0) Microsoft.Extensions.Http (5.0) Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) Microsoft.Extensions.Logging (>= 5.0) @@ -243,7 +217,6 @@ NUGET Microsoft.Extensions.Options (>= 5.0) System.Diagnostics.DiagnosticSource (>= 5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netstandard2.1)) (== netstandard2.0) Microsoft.Extensions.Logging.Abstractions (5.0) - Microsoft.Extensions.ObjectPool (5.0.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) Microsoft.Extensions.Options (5.0) Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) Microsoft.Extensions.Primitives (>= 5.0) @@ -251,24 +224,17 @@ NUGET System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (== netstandard2.0) System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) System.Runtime.CompilerServices.Unsafe (>= 5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netcoreapp3.0)) (&& (== net50) (< netstandard2.0) (>= xamarintvos)) (&& (== net50) (< netstandard2.0) (>= xamarinwatchos)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - Microsoft.Extensions.WebEncoders (5.0.5) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.DependencyInjection.Abstractions (>= 5.0) - Microsoft.Extensions.Options (>= 5.0) - System.Text.Encodings.Web (>= 5.0.1) - restriction: || (&& (== net50) (>= net461)) (== netstandard2.0) - Microsoft.IO.RecyclableMemoryStream (2.0) - Microsoft.Net.Http.Headers (2.2.8) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - Microsoft.Extensions.Primitives (>= 2.2) - System.Buffers (>= 4.5) - Microsoft.NET.Test.Sdk (16.9.4) - Microsoft.CodeCoverage (>= 16.9.4) - restriction: || (== net50) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp1.0)) - Microsoft.TestPlatform.TestHost (>= 16.9.4) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) + Microsoft.IO.RecyclableMemoryStream (2.1.3) - restriction: || (== net50) (&& (== netstandard2.0) (>= net50)) + Microsoft.NET.Test.Sdk (16.11) + Microsoft.CodeCoverage (>= 16.11) - restriction: || (== net50) (&& (== netstandard2.0) (>= net45)) (&& (== netstandard2.0) (>= netcoreapp1.0)) + Microsoft.TestPlatform.TestHost (>= 16.11) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) Microsoft.NETCore.Platforms (5.0.2) Microsoft.NETCore.Targets (5.0) - Microsoft.TestPlatform.ObjectModel (16.9.4) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) + Microsoft.TestPlatform.ObjectModel (16.11) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) NuGet.Frameworks (>= 5.0) System.Reflection.Metadata (>= 1.6) - Microsoft.TestPlatform.TestHost (16.9.4) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) - Microsoft.TestPlatform.ObjectModel (>= 16.9.4) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) (&& (== netstandard2.0) (>= uap10.0)) + Microsoft.TestPlatform.TestHost (16.11) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) + Microsoft.TestPlatform.ObjectModel (>= 16.11) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) (&& (== netstandard2.0) (>= uap10.0)) Newtonsoft.Json (>= 9.0.1) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) (&& (== netstandard2.0) (>= uap10.0)) Microsoft.Win32.Registry (5.0) System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (>= xamarinios)) (&& (== net50) (>= xamarinmac)) (&& (== net50) (>= xamarintvos)) (&& (== net50) (>= xamarinwatchos)) (== netstandard2.0) @@ -278,9 +244,12 @@ NUGET NETStandard.Library (2.0.3) Microsoft.NETCore.Platforms (>= 1.1) Newtonsoft.Json (13.0.1) - NuGet.Frameworks (5.9) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) + NuGet.Frameworks (5.11) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp1.0)) (&& (== netstandard2.0) (>= netcoreapp2.1)) Perfolizer (0.2.1) System.Memory (>= 4.5.3) + Ply (0.3.1) - restriction: || (== net50) (&& (== netstandard2.0) (>= net50)) + FSharp.Core (>= 4.6.2) + System.Threading.Tasks.Extensions (>= 4.5.4) runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) runtime.debian.9-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) @@ -321,7 +290,7 @@ NUGET runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) runtime.ubuntu.18.04-x64.runtime.native.System.Security.Cryptography.OpenSsl (4.3.3) - Suave (2.6) + Suave (2.6.1) FSharp.Core - restriction: || (== net50) (&& (== netstandard2.0) (>= netstandard2.1)) System.Buffers (4.5.1) System.CodeDom (5.0) @@ -353,7 +322,7 @@ NUGET Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) System.Runtime (>= 4.3) - System.Formats.Asn1 (5.0) + System.Formats.Asn1 (5.0) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp3.0)) System.Globalization (4.3) Microsoft.NETCore.Platforms (>= 1.1) Microsoft.NETCore.Targets (>= 1.1) @@ -387,10 +356,6 @@ NUGET System.Threading.Tasks (>= 4.3) System.IO.FileSystem.Primitives (4.3) System.Runtime (>= 4.3) - System.IO.Pipelines (5.0.1) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= xamarinios)) (&& (== net50) (>= xamarinmac)) (&& (== net50) (>= xamarintvos)) (&& (== net50) (>= xamarinwatchos)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Threading.Tasks.Extensions (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) System.Linq (4.3) System.Collections (>= 4.3) System.Diagnostics.Debug (>= 4.3) @@ -539,11 +504,6 @@ NUGET System.Text.Encoding (>= 4.3) System.Security.Cryptography.OpenSsl (5.0) System.Formats.Asn1 (>= 5.0) - restriction: || (== net50) (&& (== netstandard2.0) (>= netcoreapp3.0)) - System.Security.Cryptography.Pkcs (5.0.1) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (== netstandard2.0) - System.Formats.Asn1 (>= 5.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) - System.Security.Cryptography.Cng (>= 5.0) System.Security.Cryptography.Primitives (4.3) System.Diagnostics.Debug (>= 4.3) System.Globalization (>= 4.3) @@ -578,12 +538,6 @@ NUGET System.Security.Cryptography.Primitives (>= 4.3) System.Text.Encoding (>= 4.3) System.Threading (>= 4.3) - System.Security.Cryptography.Xml (5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (< netcoreapp2.1)) (== netstandard2.0) - System.Security.Cryptography.Pkcs (>= 5.0) - System.Security.Permissions (>= 5.0) - System.Security.Permissions (5.0) - restriction: || (&& (== net50) (>= monotouch)) (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - System.Security.AccessControl (>= 5.0) System.Security.Principal.Windows (5.0) System.Text.Encoding (4.3) Microsoft.NETCore.Platforms (>= 1.1) @@ -592,9 +546,7 @@ NUGET System.Text.Encoding.CodePages (5.0) Microsoft.NETCore.Platforms (>= 5.0) - restriction: || (== net50) (&& (== netstandard2.0) (>= net50)) (&& (== netstandard2.0) (>= netcoreapp2.0)) System.Runtime.CompilerServices.Unsafe (>= 5.0) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0)) (== netstandard2.0) - System.Text.Encodings.Web (5.0.1) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp3.0)) (== netstandard2.0) - System.Buffers (>= 4.5.1) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (== netstandard2.0) - System.Memory (>= 4.5.4) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.0) (< netstandard2.1)) (&& (== net50) (< netcoreapp2.1) (< netstandard2.1)) (&& (== net50) (>= uap10.1)) (== netstandard2.0) + System.Text.Json (5.0.2) - restriction: || (== net50) (&& (== netstandard2.0) (>= net50)) System.Threading (4.3) System.Runtime (>= 4.3) System.Threading.Tasks (>= 4.3) @@ -605,11 +557,7 @@ NUGET System.Threading.Tasks.Extensions (4.5.4) System.Runtime.CompilerServices.Unsafe (>= 4.5.3) - restriction: || (&& (== net50) (>= net461)) (&& (== net50) (< netcoreapp2.1)) (&& (== net50) (< netstandard1.0)) (&& (== net50) (< netstandard2.0)) (&& (== net50) (>= wp8)) (== netstandard2.0) System.ValueTuple (4.5) - TaskBuilder.fs (2.1) - FSharp.Core (>= 4.1.17) - NETStandard.Library (>= 1.6.1) - System.ValueTuple (>= 4.4) - Utf8Json (1.3.7) + Utf8Json (1.3.7) - restriction: || (== net50) (&& (== netstandard2.0) (>= net50)) System.Reflection.Emit (>= 4.3) System.Reflection.Emit.Lightweight (>= 4.3) System.Threading.Tasks.Extensions (>= 4.4) diff --git a/samples/client-provider/field_aliases.fsx b/samples/client-provider/field_aliases.fsx index 538242a51..d10dc87ac 100644 --- a/samples/client-provider/field_aliases.fsx +++ b/samples/client-provider/field_aliases.fsx @@ -1,19 +1,5 @@ -// Uncomment those to use build script client assembly -//#r "../../bin/FSharp.Data.GraphQL.Client/net47/FSharp.Data.GraphQL.Client.dll" -//#r "../../bin/FSharp.Data.GraphQL.Shared/net47/FSharp.Data.GraphQL.Shared.dll" - -// Uncomment those to use build script client assembly using netstandard2.0 -//#r "../../bin/FSharp.Data.GraphQL.Shared/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/netstandard.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/FSharp.Data.GraphQL.Client.dll" - -// Uncomment those to use dotnet build command for the client assembly -// #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/net47/FSharp.Data.GraphQL.Shared.dll" -// #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/net47/FSharp.Data.GraphQL.Client.dll" - -//Uncomment those to use dotnet build command for the client assembly using netstandard2.0 +#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/typeproviders/fsharp41/netstandard2.0/Microsoft.Extensions.Http.dll" #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -//#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/netstandard.dll" #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Client.dll" open FSharp.Data.GraphQL diff --git a/samples/client-provider/file-upload/README.md b/samples/client-provider/file-upload/README.md deleted file mode 100644 index bc9383937..000000000 --- a/samples/client-provider/file-upload/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# TypeProvider file upload sample - -In this folder you can run a sample which allows the client type provider to upload files to a GraphQL server using a [Multipart Request Spec](https://github.com/jaydenseric/graphql-multipart-request-spec) used by common server implementations, such as Apollo. - -To run the sample, first go to the (server folder)[server]. Be sure that Node.js and npm are installed in your machine, and run `npm install` to install packages. After that, run `npm run build && npm start` to start the Apollo server. Then, run one of the sample fsx files: - -1. [uploaded_files.fsx](uploaded_files.fsx): This script allows you to run a query to see the uploaded files on the server. -2. [single_file_upload.fsx](single_file_upload.fsx): This script shows an example of uploading a single file to the server using a mutation operation. -3. [multiple_files_upload.fsx](multiple_files_upload.fsx): This script shows an example of uploading multiple files to the server using a mutation operation. \ No newline at end of file diff --git a/samples/client-provider/file-upload/multiple_files_upload.fsx b/samples/client-provider/file-upload/multiple_files_upload.fsx deleted file mode 100644 index 40f751644..000000000 --- a/samples/client-provider/file-upload/multiple_files_upload.fsx +++ /dev/null @@ -1,44 +0,0 @@ -// Uncomment those to use build script client assembly -//#r "../../../bin/FSharp.Data.GraphQL.Client/net47/FSharp.Data.GraphQL.Client.dll" -//#r "../../../bin/FSharp.Data.GraphQL.Shared/net47/FSharp.Data.GraphQL.Shared.dll" - -// Uncomment those to use build script client assembly using netstandard2.0 -//#r "../../../bin/FSharp.Data.GraphQL.Shared/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -//#r "../../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/netstandard.dll" -//#r "../../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/FSharp.Data.GraphQL.Client.dll" - -// Uncomment those to use dotnet build command for the client assembly -// #r "../../../src/FSharp.Data.GraphQL.Shared/bin/Debug/net47/FSharp.Data.GraphQL.Shared.dll" -// #r "../../../src/FSharp.Data.GraphQL.Client/bin/Debug/net47/FSharp.Data.GraphQL.Client.dll" - -//Uncomment those to use dotnet build command for the client assembly using netstandard2.0 -#r "../../../src/FSharp.Data.GraphQL.Shared/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -#r "../../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/netstandard.dll" -#r "../../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Client.dll" - -open System -open System.IO -open FSharp.Data.GraphQL - -type MyProvider = GraphQLProvider<"http://localhost:3001/graphql", uploadInputTypeName = "Upload"> - -let mutation = - MyProvider.Operation<"""mutation multipleFilesUpload($request: MultipleUploadRequest!) { - multipleUpload(request: $request) { - id - path - filename - mimetype - } - }""">() - -let upload() = - let input = - [| new Upload(File.OpenRead("txt_file.txt"), "text.txt", ownsStream = true) - new Upload(File.OpenRead("png_file.png"), "image.png", ownsStream = true) |] - let request = MyProvider.Types.MultipleUploadRequest(input) - let result = mutation.Run(request) - input |> Array.iter (fun x -> (x :> IDisposable).Dispose()) - printfn "Data: %A" result.Data - -upload() \ No newline at end of file diff --git a/samples/client-provider/file-upload/server/.env b/samples/client-provider/file-upload/server/.env deleted file mode 100644 index f3e5e83b6..000000000 --- a/samples/client-provider/file-upload/server/.env +++ /dev/null @@ -1,2 +0,0 @@ -NODE_ENV=development -PORT=3001 \ No newline at end of file diff --git a/samples/client-provider/file-upload/server/.gitignore b/samples/client-provider/file-upload/server/.gitignore deleted file mode 100644 index bfe0fa6b1..000000000 --- a/samples/client-provider/file-upload/server/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -db.json -/uploads diff --git a/samples/client-provider/file-upload/server/package-lock.json b/samples/client-provider/file-upload/server/package-lock.json deleted file mode 100644 index 3e1f47145..000000000 --- a/samples/client-provider/file-upload/server/package-lock.json +++ /dev/null @@ -1,5033 +0,0 @@ -{ - "name": "apollo-upload-examples-api", - "requires": true, - "lockfileVersion": 1, - "dependencies": { - "@apollographql/apollo-tools": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@apollographql/apollo-tools/-/apollo-tools-0.3.5.tgz", - "integrity": "sha512-5ySiiNT2EIwxGKWyoAOnibCPUXvbxKOVxiPMK4uIXmvF+qbGNleQWP+vekciiAmCCESPmGd5szscRwDm4G/NNg==", - "requires": { - "apollo-env": "0.4.0" - } - }, - "@apollographql/graphql-playground-html": { - "version": "1.6.6", - "resolved": "https://registry.npmjs.org/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.6.tgz", - "integrity": "sha512-lqK94b+caNtmKFs5oUVXlSpN3sm5IXZ+KfhMxOtr0LR2SqErzkoJilitjDvJ1WbjHlxLI7WtCjRmOLdOGJqtMQ==" - }, - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "@koa/cors": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@koa/cors/-/cors-2.2.3.tgz", - "integrity": "sha512-tCVVXa39ETsit5kGBtEWWimjLn1sDaeu8+0phgb8kT3GmBDZOykkI3ZO8nMjV2p3MGkJI4K5P+bxR8Ztq0bwsA==", - "requires": { - "vary": "^1.1.2" - } - }, - "@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" - }, - "@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" - }, - "@protobufjs/codegen": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", - "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" - }, - "@protobufjs/eventemitter": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", - "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" - }, - "@protobufjs/fetch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", - "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", - "requires": { - "@protobufjs/aspromise": "^1.1.1", - "@protobufjs/inquire": "^1.1.0" - } - }, - "@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" - }, - "@protobufjs/inquire": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", - "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" - }, - "@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" - }, - "@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" - }, - "@protobufjs/utf8": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", - "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" - }, - "@types/accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", - "requires": { - "@types/node": "*" - } - }, - "@types/body-parser": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", - "integrity": "sha512-a2+YeUjPkztKJu5aIF2yArYFQQp8d51wZ7DavSHjFuY1mqVgidGyzEQ41JIVNy82fXj8yPgy2vJmfIywgESW6w==", - "requires": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "@types/connect": { - "version": "3.4.32", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", - "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", - "requires": { - "@types/node": "*" - } - }, - "@types/cookies": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.1.tgz", - "integrity": "sha512-ku6IvbucEyuC6i4zAVK/KnuzWNXdbFd1HkXlNLg/zhWDGTtQT5VhumiPruB/BHW34PWVFwyfwGftDQHfWNxu3Q==", - "requires": { - "@types/connect": "*", - "@types/express": "*", - "@types/keygrip": "*", - "@types/node": "*" - } - }, - "@types/cors": { - "version": "2.8.4", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.4.tgz", - "integrity": "sha512-ipZjBVsm2tF/n8qFGOuGBkUij9X9ZswVi9G3bx/6dz7POpVa6gVHcj1wsX/LVEn9MMF41fxK/PnZPPoTD1UFPw==", - "requires": { - "@types/express": "*" - } - }, - "@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==" - }, - "@types/express": { - "version": "4.16.1", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.16.1.tgz", - "integrity": "sha512-V0clmJow23WeyblmACoxbHBu2JKlE5TiIme6Lem14FnPW9gsttyHtk6wq7njcdIWH1njAaFgR8gW09lgY98gQg==", - "requires": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "*", - "@types/serve-static": "*" - } - }, - "@types/express-serve-static-core": { - "version": "4.16.2", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.2.tgz", - "integrity": "sha512-qgc8tjnDrc789rAQed8NoiFLV5VGcItA4yWNFphqGU0RcuuQngD00g3LHhWIK3HQ2XeDgVCmlNPDlqi3fWBHnQ==", - "requires": { - "@types/node": "*", - "@types/range-parser": "*" - } - }, - "@types/http-assert": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.4.0.tgz", - "integrity": "sha512-TZDqvFW4nQwL9DVSNJIJu4lPLttKgzRF58COa7Vs42Ki/MrhIqUbeIw0MWn4kGLiZLXB7oCBibm7nkSjPkzfKQ==" - }, - "@types/keygrip": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.1.tgz", - "integrity": "sha1-/1QEYtL7TQqIRBzq8n0oewHD2Hg=" - }, - "@types/koa": { - "version": "2.0.48", - "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.0.48.tgz", - "integrity": "sha512-CiIUYhHlOFJhSCTmsFoFkV2t9ij1JwW26nt0W9XZoWTvmAw6zTE0+k3IAoGICtjzIfhZpZcO323NHmI1LGmdDw==", - "requires": { - "@types/accepts": "*", - "@types/cookies": "*", - "@types/http-assert": "*", - "@types/keygrip": "*", - "@types/koa-compose": "*", - "@types/node": "*" - } - }, - "@types/koa-bodyparser": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@types/koa-bodyparser/-/koa-bodyparser-4.2.2.tgz", - "integrity": "sha512-liOoUnpv7V+NX46kAqYDh503t0PI0NFjALn+L8IAlS/mdwOAg9jJoso88mN22MaWt7hLdQ+Z/MMwb9RglhEAcQ==", - "requires": { - "@types/koa": "*" - } - }, - "@types/koa-compose": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.3.tgz", - "integrity": "sha512-kXvR0DPyZ3gaFxZs4WycA8lpzlPGtFmwdbgce+NWd+TG3PycPO3o5FkePH60HoBPd8BBaSiw3vhtgM42O2kQcg==", - "requires": { - "@types/koa": "*" - } - }, - "@types/koa__cors": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@types/koa__cors/-/koa__cors-2.2.3.tgz", - "integrity": "sha512-RfG2EuSc+nv/E+xbDSLW8KCoeri/3AkqwVPuENfF/DctllRoXhooboO//Sw7yFtkLvj7nG7O1H3JcZmoTQz8nQ==", - "requires": { - "@types/koa": "*" - } - }, - "@types/long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz", - "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q==" - }, - "@types/mime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", - "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==" - }, - "@types/node": { - "version": "11.13.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-11.13.4.tgz", - "integrity": "sha512-+rabAZZ3Yn7tF/XPGHupKIL5EcAbrLxnTr/hgQICxbeuAfWtT0UZSfULE+ndusckBItcv4o6ZeOJplQikVcLvQ==" - }, - "@types/range-parser": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", - "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" - }, - "@types/serve-static": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.2.tgz", - "integrity": "sha512-/BZ4QRLpH/bNYgZgwhKEh+5AsboDBcUdlBYgzoLX0fpj3Y2gp6EApyOlM3bK53wQS/OE1SrdSYBAbux2D1528Q==", - "requires": { - "@types/express-serve-static-core": "*", - "@types/mime": "*" - } - }, - "@types/ws": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-6.0.1.tgz", - "integrity": "sha512-EzH8k1gyZ4xih/MaZTXwT2xOkPiIMSrhQ9b8wrlX88L0T02eYsddatQlwVFlEPyEqV0ChpdpNnE51QPH6NVT4Q==", - "requires": { - "@types/events": "*", - "@types/node": "*" - } - }, - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true - }, - "accepts": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", - "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", - "requires": { - "mime-types": "~2.1.18", - "negotiator": "0.6.1" - } - }, - "acorn": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", - "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", - "dev": true - }, - "acorn-jsx": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", - "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==", - "dev": true - }, - "ajv": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", - "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-align": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", - "dev": true, - "requires": { - "string-width": "^2.0.0" - } - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "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" - } - }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" - }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "apollo-cache-control": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/apollo-cache-control/-/apollo-cache-control-0.5.2.tgz", - "integrity": "sha512-uehXDUrd3Qim+nzxqqN7XT1YTbNSyumW3/FY5BxbKZTI8d4oPG4eyVQKqaggooSjswKQnOoIQVes3+qg9tGAkw==", - "requires": { - "apollo-server-env": "2.2.0", - "graphql-extensions": "0.5.4" - }, - "dependencies": { - "graphql-extensions": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/graphql-extensions/-/graphql-extensions-0.5.4.tgz", - "integrity": "sha512-qLThJGVMqcItE7GDf/xX/E40m/aeqFheEKiR5bfra4q5eHxQKGjnIc20P9CVqjOn9I0FkEiU9ypOobfmIf7t6g==", - "requires": { - "@apollographql/apollo-tools": "^0.3.3" - } - } - } - }, - "apollo-datasource": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/apollo-datasource/-/apollo-datasource-0.3.1.tgz", - "integrity": "sha512-qdEUeonc9pPZvYwXK36h2NZoT7Pddmy0HYOzdV0ON5pcG1YtNmUyyYi83Q60V5wTWjuaCjyJ9hOY6wr0BMvQuA==", - "requires": { - "apollo-server-caching": "0.3.1", - "apollo-server-env": "2.2.0" - } - }, - "apollo-engine-reporting": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/apollo-engine-reporting/-/apollo-engine-reporting-1.0.7.tgz", - "integrity": "sha512-mFsXvd+1/o5jSa9tI2RoXYGcvCLcwwcfLwchjSTxqUd4ViB8RbqYKynzEZ+Omji7PBRM0azioBm43f7PSsQPqA==", - "requires": { - "apollo-engine-reporting-protobuf": "0.2.1", - "apollo-graphql": "^0.1.0", - "apollo-server-core": "2.4.8", - "apollo-server-env": "2.2.0", - "async-retry": "^1.2.1", - "graphql-extensions": "0.5.7" - } - }, - "apollo-engine-reporting-protobuf": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/apollo-engine-reporting-protobuf/-/apollo-engine-reporting-protobuf-0.2.1.tgz", - "integrity": "sha512-5pYR84uWeylRS2OJowtkTczT3bWTwOErWtfnkRKccUi/wZ/AZJBP+D5HKNzM7xoFcz9XvrJyS+wBTz1oBi0Jiw==", - "requires": { - "protobufjs": "^6.8.6" - } - }, - "apollo-env": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/apollo-env/-/apollo-env-0.4.0.tgz", - "integrity": "sha512-TZpk59RTbXd8cEqwmI0KHFoRrgBRplvPAP4bbRrX4uDSxXvoiY0Y6tQYUlJ35zi398Hob45mXfrZxeRDzoFMkQ==", - "requires": { - "core-js": "3.0.0-beta.13", - "node-fetch": "^2.2.0", - "sha.js": "^2.4.11" - } - }, - "apollo-graphql": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/apollo-graphql/-/apollo-graphql-0.1.3.tgz", - "integrity": "sha512-bYgDh71jFfHKO9ioGlxnnoSYgpNp6LRl+/QHTx6tktQEN0Z+AdpkOKFNCHO/pRU/4vSqV5wuIhxhnCecxJQrMA==", - "requires": { - "apollo-env": "0.4.0", - "lodash.sortby": "^4.7.0" - } - }, - "apollo-link": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.11.tgz", - "integrity": "sha512-PQvRCg13VduLy3X/0L79M6uOpTh5iHdxnxYuo8yL7sJlWybKRJwsv4IcRBJpMFbChOOaHY7Og9wgPo6DLKDKDA==", - "requires": { - "apollo-utilities": "^1.2.1", - "ts-invariant": "^0.3.2", - "tslib": "^1.9.3", - "zen-observable-ts": "^0.8.18" - } - }, - "apollo-server-caching": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/apollo-server-caching/-/apollo-server-caching-0.3.1.tgz", - "integrity": "sha512-mfxzikYXbB/OoEms77AGYwRh7FF3Oim5v5XWAL+VL49FrkbZt5lopVa4bABi7Mz8Nt3Htl9EBJN8765s/yh8IA==", - "requires": { - "lru-cache": "^5.0.0" - } - }, - "apollo-server-core": { - "version": "2.4.8", - "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-2.4.8.tgz", - "integrity": "sha512-N+5UOzHhMOnHizEiArJtNvEe/cGhSHQyTn5tlU4RJ36FDBJ/WlYZfPbGDMLISSUCJ6t+aP8GLL4Mnudt9d2PDQ==", - "requires": { - "@apollographql/apollo-tools": "^0.3.3", - "@apollographql/graphql-playground-html": "^1.6.6", - "@types/ws": "^6.0.0", - "apollo-cache-control": "0.5.2", - "apollo-datasource": "0.3.1", - "apollo-engine-reporting": "1.0.7", - "apollo-server-caching": "0.3.1", - "apollo-server-env": "2.2.0", - "apollo-server-errors": "2.2.1", - "apollo-server-plugin-base": "0.3.7", - "apollo-tracing": "0.5.2", - "fast-json-stable-stringify": "^2.0.0", - "graphql-extensions": "0.5.7", - "graphql-subscriptions": "^1.0.0", - "graphql-tag": "^2.9.2", - "graphql-tools": "^4.0.0", - "graphql-upload": "^8.0.2", - "sha.js": "^2.4.11", - "subscriptions-transport-ws": "^0.9.11", - "ws": "^6.0.0" - } - }, - "apollo-server-env": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/apollo-server-env/-/apollo-server-env-2.2.0.tgz", - "integrity": "sha512-wjJiI5nQWPBpNmpiLP389Ezpstp71szS6DHAeTgYLb/ulCw3CTuuA+0/E1bsThVWiQaDeHZE0sE3yI8q2zrYiA==", - "requires": { - "node-fetch": "^2.1.2", - "util.promisify": "^1.0.0" - } - }, - "apollo-server-errors": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/apollo-server-errors/-/apollo-server-errors-2.2.1.tgz", - "integrity": "sha512-wY/YE3iJVMYC+WYIf8QODBjIP4jhI+oc7kiYo9mrz7LdYPKAgxr/he+NteGcqn/0Ea9K5/ZFTGJDbEstSMeP8g==" - }, - "apollo-server-koa": { - "version": "2.4.8", - "resolved": "https://registry.npmjs.org/apollo-server-koa/-/apollo-server-koa-2.4.8.tgz", - "integrity": "sha512-0zHDhJ8CigTUPfWS3hXdapkl1dQgw34TZYCW2ca1GFzOGGjHEkKPLXis7m6J7eVETwDaugRkp9f2IiGPyrXZsg==", - "requires": { - "@apollographql/graphql-playground-html": "^1.6.6", - "@koa/cors": "^2.2.1", - "@types/accepts": "^1.3.5", - "@types/cors": "^2.8.4", - "@types/koa": "^2.0.46", - "@types/koa-bodyparser": "^4.2.1", - "@types/koa-compose": "^3.2.2", - "@types/koa__cors": "^2.2.1", - "accepts": "^1.3.5", - "apollo-server-core": "2.4.8", - "graphql-subscriptions": "^1.0.0", - "graphql-tools": "^4.0.0", - "koa": "2.7.0", - "koa-bodyparser": "^3.0.0", - "koa-router": "^7.4.0", - "type-is": "^1.6.16" - } - }, - "apollo-server-plugin-base": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/apollo-server-plugin-base/-/apollo-server-plugin-base-0.3.7.tgz", - "integrity": "sha512-hW1jaLKf9qNOxMTwRq2CSqz3eqXsZuEiCc8/mmEtOciiVBq1GMtxFf19oIYM9HQuPvQU2RWpns1VrYN59L3vbg==" - }, - "apollo-tracing": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/apollo-tracing/-/apollo-tracing-0.5.2.tgz", - "integrity": "sha512-2FdwRvPIq9uuF6OzONroXep6VBGqzHOkP6LlcFQe7SdwxfRP+SD/ycHNSC1acVg2b8d+am9Kzqg2vV54UpOIKA==", - "requires": { - "apollo-server-env": "2.2.0", - "graphql-extensions": "0.5.4" - }, - "dependencies": { - "graphql-extensions": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/graphql-extensions/-/graphql-extensions-0.5.4.tgz", - "integrity": "sha512-qLThJGVMqcItE7GDf/xX/E40m/aeqFheEKiR5bfra4q5eHxQKGjnIc20P9CVqjOn9I0FkEiU9ypOobfmIf7t6g==", - "requires": { - "@apollographql/apollo-tools": "^0.3.3" - } - } - } - }, - "apollo-utilities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.2.1.tgz", - "integrity": "sha512-Zv8Udp9XTSFiN8oyXOjf6PMHepD4yxxReLsl6dPUy5Ths7jti3nmlBzZUOxuTWRwZn0MoclqL7RQ5UEJN8MAxg==", - "requires": { - "fast-json-stable-stringify": "^2.0.0", - "ts-invariant": "^0.2.1", - "tslib": "^1.9.3" - }, - "dependencies": { - "ts-invariant": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.2.1.tgz", - "integrity": "sha512-Z/JSxzVmhTo50I+LKagEISFJW3pvPCqsMWLamCTX8Kr3N5aMrnGOqcflbe5hLUzwjvgPfnLzQtHZv0yWQ+FIHg==", - "requires": { - "tslib": "^1.9.3" - } - } - } - }, - "app-root-path": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.2.1.tgz", - "integrity": "sha512-91IFKeKk7FjfmezPKkwtaRvSpnUc4gDwPAjA1YZ9Gn0q0PPeW+vbeUsZuyDwjI7+QTHhcLen2v25fi/AmhvbJA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "async-each": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.2.tgz", - "integrity": "sha512-6xrbvN0MOBKSJDdonmSSz2OwFSgxRaVtBDes26mj9KIGtDo+g9xosFRSC+i1gQh2oAN/tQ62AI/pGZGQjVOiRg==", - "dev": true - }, - "async-limiter": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" - }, - "async-retry": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.2.3.tgz", - "integrity": "sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q==", - "requires": { - "retry": "0.12.0" - } - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "backo2": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true - }, - "bluebird": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", - "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==" - }, - "boxen": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", - "dev": true, - "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" - } - }, - "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": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "busboy": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-0.3.1.tgz", - "integrity": "sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw==", - "requires": { - "dicer": "0.3.0" - } - }, - "bytes": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.4.0.tgz", - "integrity": "sha1-fZcZb51br39pNeJZhVSe3SpsIzk=" - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "cache-content-type": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", - "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", - "requires": { - "mime-types": "^2.1.18", - "ylru": "^1.2.0" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "capture-stack-trace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", - "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", - "dev": true - }, - "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" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "chokidar": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz", - "integrity": "sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "ci-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", - "dev": true - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" - }, - "co-body": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/co-body/-/co-body-4.2.0.tgz", - "integrity": "sha1-dN8g+nMmISXcRUgq8E40LqjbNRU=", - "requires": { - "inflation": "~2.0.0", - "qs": "~4.0.0", - "raw-body": "~2.1.2", - "type-is": "~1.6.6" - } - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.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": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "configstore": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", - "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", - "dev": true, - "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, - "content-disposition": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", - "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", - "requires": { - "safe-buffer": "5.1.2" - } - }, - "content-type": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" - }, - "cookies": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.3.tgz", - "integrity": "sha512-+gixgxYSgQLTaTIilDHAdlNPZDENDQernEMiIcZpYYP14zgHsCt4Ce1FEjFtcp6GefhozebB6orvhAAWx/IS0A==", - "requires": { - "depd": "~1.1.2", - "keygrip": "~1.0.3" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "core-js": { - "version": "3.0.0-beta.13", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.0.0-beta.13.tgz", - "integrity": "sha512-16Q43c/3LT9NyePUJKL8nRIQgYWjcBhjJSMWg96PVSxoS0PeE0NHitPI3opBrs9MGGHjte1KoEVr9W63YKlTXQ==" - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "dev": true, - "requires": { - "capture-stack-trace": "^1.0.0" - } - }, - "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" - } - }, - "crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", - "dev": true - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" - }, - "depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" - }, - "deprecated-decorator": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz", - "integrity": "sha1-AJZjF7ehL+kvPMgx91g68ym4bDc=" - }, - "destroy": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", - "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" - }, - "dicer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/dicer/-/dicer-0.3.0.tgz", - "integrity": "sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA==", - "requires": { - "streamsearch": "0.1.2" - } - }, - "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" - } - }, - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "dev": true, - "requires": { - "is-obj": "^1.0.0" - } - }, - "dotenv": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.2.0.tgz", - "integrity": "sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==" - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, - "ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "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" - } - }, - "error-inject": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/error-inject/-/error-inject-1.0.0.tgz", - "integrity": "sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc=" - }, - "es-abstract": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", - "integrity": "sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg==", - "requires": { - "es-to-primitive": "^1.2.0", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "is-callable": "^1.1.4", - "is-regex": "^1.0.4", - "object-keys": "^1.0.12" - } - }, - "es-to-primitive": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", - "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.16.0.tgz", - "integrity": "sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.9.1", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^4.0.3", - "eslint-utils": "^1.3.1", - "eslint-visitor-keys": "^1.0.0", - "espree": "^5.0.1", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob": "^7.1.2", - "globals": "^11.7.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^6.2.2", - "js-yaml": "^3.13.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.11", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "path-is-inside": "^1.0.2", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^5.5.1", - "strip-ansi": "^4.0.0", - "strip-json-comments": "^2.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - } - } - }, - "eslint-config-env": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-env/-/eslint-config-env-4.0.0.tgz", - "integrity": "sha512-gvHVIWiXEeQvQScsb6YyOtTL51pHMOwIjvpVM+qxvovF5MSfzLLovx87rSq/vzBBmLZTi9lOorUTzwV5ZrbRbA==", - "dev": true, - "requires": { - "app-root-path": "^2.1.0", - "read-pkg-up": "^4.0.0", - "semver": "^5.6.0" - } - }, - "eslint-config-prettier": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-3.6.0.tgz", - "integrity": "sha512-ixJ4U3uTLXwJts4rmSVW/lMXjlGwCijhBJHk8iVqKKSifeI0qgFEfWl8L63isfc8Od7EiBALF6BX3jKLluf/jQ==", - "dev": true, - "requires": { - "get-stdin": "^6.0.0" - } - }, - "eslint-import-resolver-node": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz", - "integrity": "sha512-sfmTqJfPSizWu4aymbPr4Iidp5yKm8yDkHp+Ir3YiTHiiDfxh69mOUsmiqW6RZ9zRXFaF64GtYmN7e+8GHBv6Q==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.5.0" - }, - "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" - } - } - } - }, - "eslint-module-utils": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.3.0.tgz", - "integrity": "sha512-lmDJgeOOjk8hObTysjqH7wyMi+nsHwwvfBykwfhjR1LNdd7C2uFJBvx4OpWYpXOw4df1yE1cDEVd1yLHitk34w==", - "dev": true, - "requires": { - "debug": "^2.6.8", - "pkg-dir": "^2.0.0" - }, - "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" - } - } - } - }, - "eslint-plugin-es": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-1.4.0.tgz", - "integrity": "sha512-XfFmgFdIUDgvaRAlaXUkxrRg5JSADoRC8IkKLc/cISeR3yHVMefFHQZpcyXXEUUPHfy5DwviBcrfqlyqEwlQVw==", - "dev": true, - "requires": { - "eslint-utils": "^1.3.0", - "regexpp": "^2.0.1" - } - }, - "eslint-plugin-import": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.16.0.tgz", - "integrity": "sha512-z6oqWlf1x5GkHIFgrSvtmudnqM6Q60KM4KvpWi5ubonMjycLjndvd5+8VAZIsTlHC03djdgJuyKG6XO577px6A==", - "dev": true, - "requires": { - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.2", - "eslint-module-utils": "^2.3.0", - "has": "^1.0.3", - "lodash": "^4.17.11", - "minimatch": "^3.0.4", - "read-pkg-up": "^2.0.0", - "resolve": "^1.9.0" - }, - "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": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - } - } - }, - "eslint-plugin-import-order-alphabetical": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-import-order-alphabetical/-/eslint-plugin-import-order-alphabetical-0.0.2.tgz", - "integrity": "sha512-9WBxC3RzQrJTAJ/epl64k4pvug/Le1OvhydICoQydK/2M4+36lnAlsn/3dsvXR+1WnRuc2mziNTkFUs+tx+22w==", - "dev": true, - "requires": { - "eslint-module-utils": "^2.2.0", - "lodash": "^4.17.4", - "resolve": "^1.6.0" - } - }, - "eslint-plugin-node": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-8.0.1.tgz", - "integrity": "sha512-ZjOjbjEi6jd82rIpFSgagv4CHWzG9xsQAVp1ZPlhRnnYxcTgENUVBvhYmkQ7GvT1QFijUSo69RaiOJKhMu6i8w==", - "dev": true, - "requires": { - "eslint-plugin-es": "^1.3.1", - "eslint-utils": "^1.3.1", - "ignore": "^5.0.2", - "minimatch": "^3.0.4", - "resolve": "^1.8.1", - "semver": "^5.5.0" - }, - "dependencies": { - "ignore": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.0.6.tgz", - "integrity": "sha512-/+hp3kUf/Csa32ktIaj0OlRqQxrgs30n62M90UBpNd9k+ENEch5S+hmbW3DtcJGz3sYFTh4F3A6fQ0q7KWsp4w==", - "dev": true - } - } - }, - "eslint-plugin-prettier": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.0.1.tgz", - "integrity": "sha512-/PMttrarPAY78PLvV3xfWibMOdMDl57hmlQ2XqFeA37wd+CJ7WSxV7txqjVPHi/AAFKd2lX0ZqfsOc/i5yFCSQ==", - "dev": true, - "requires": { - "prettier-linter-helpers": "^1.0.0" - } - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", - "dev": true - }, - "eslint-visitor-keys": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", - "dev": true - }, - "espree": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", - "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==", - "dev": true, - "requires": { - "acorn": "^6.0.7", - "acorn-jsx": "^5.0.0", - "eslint-visitor-keys": "^1.0.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", - "dev": true, - "requires": { - "estraverse": "^4.0.0" - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "eventemitter3": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==" - }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", - "dev": true - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.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" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "external-editor": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", - "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "fast-diff": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", - "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "dev": true, - "requires": { - "flat-cache": "^2.0.1" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "dev": true, - "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - } - }, - "flatted": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", - "integrity": "sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg==", - "dev": true - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" - }, - "fs-capacitor": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fs-capacitor/-/fs-capacitor-2.0.1.tgz", - "integrity": "sha512-kyV2oaG1/pu9NPosfGACmBym6okgzyg6hEtA5LSUq0dGpGLe278MVfMwVnSHDA/OBcTCHkPNqWL9eIwbPN6dDg==" - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", - "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.2.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.10.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", - "dev": true, - "requires": { - "ini": "^1.3.4" - } - }, - "globals": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", - "integrity": "sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw==", - "dev": true - }, - "got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", - "dev": true, - "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" - }, - "graphql": { - "version": "14.2.1", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-14.2.1.tgz", - "integrity": "sha512-2PL1UbvKeSjy/lUeJqHk+eR9CvuErXoCNwJI4jm3oNFEeY+9ELqHNKO1ZuSxAkasPkpWbmT/iMRMFxd3cEL3tQ==", - "requires": { - "iterall": "^1.2.2" - } - }, - "graphql-extensions": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/graphql-extensions/-/graphql-extensions-0.5.7.tgz", - "integrity": "sha512-HrU6APE1PiehZ46scMB3S5DezSeCATd8v+e4mmg2bqszMyCFkmAnmK6hR1b5VjHxhzt5/FX21x1WsXfqF4FwdQ==", - "requires": { - "@apollographql/apollo-tools": "^0.3.3" - } - }, - "graphql-subscriptions": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/graphql-subscriptions/-/graphql-subscriptions-1.1.0.tgz", - "integrity": "sha512-6WzlBFC0lWmXJbIVE8OgFgXIP4RJi3OQgTPa0DVMsDXdpRDjTsM1K9wfl5HSYX7R87QAGlvcv2Y4BIZa/ItonA==", - "requires": { - "iterall": "^1.2.1" - } - }, - "graphql-tag": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.1.tgz", - "integrity": "sha512-jApXqWBzNXQ8jYa/HLkZJaVw9jgwNqZkywa2zfFn16Iv1Zb7ELNHkJaXHR7Quvd5SIGsy6Ny7SUKATgnu05uEg==" - }, - "graphql-tools": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/graphql-tools/-/graphql-tools-4.0.4.tgz", - "integrity": "sha512-chF12etTIGVVGy3fCTJ1ivJX2KB7OSG4c6UOJQuqOHCmBQwTyNgCDuejZKvpYxNZiEx7bwIjrodDgDe9RIkjlw==", - "requires": { - "apollo-link": "^1.2.3", - "apollo-utilities": "^1.0.1", - "deprecated-decorator": "^0.1.6", - "iterall": "^1.1.3", - "uuid": "^3.1.0" - } - }, - "graphql-upload": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/graphql-upload/-/graphql-upload-8.0.5.tgz", - "integrity": "sha512-iv8R/E1b0GJ203Z2sdPgnCnU8tl9hQY+jkebiTNAjsWBT3j/I5VLBnPJdDhJSKIreWJ4/1LZjgOt60qjnH4/EQ==", - "requires": { - "busboy": "^0.3.0", - "fs-capacitor": "^2.0.1", - "http-errors": "^1.7.2", - "object-path": "^0.11.4" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==", - "dev": true - }, - "http-assert": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.4.0.tgz", - "integrity": "sha512-tPVv62a6l3BbQoM/N5qo969l0OFxqpnQzNUPeYfTP6Spo4zkgWeDBD1D5thI7sDLg7jCCihXTLB0X8UtdyAy8A==", - "requires": { - "deep-equal": "~1.0.1", - "http-errors": "~1.7.1" - } - }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "iconv-lite": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.13.tgz", - "integrity": "sha1-H4irpKsLFQjoMSrMOTRfNumS4vI=" - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", - "dev": true - }, - "import-fresh": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz", - "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflation": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/inflation/-/inflation-2.0.0.tgz", - "integrity": "sha1-i0F+R8KPklpFEz2RTKH9OJEH8w8=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "inquirer": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz", - "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==", - "dev": true, - "requires": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.11", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.0.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-callable": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", - "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" - }, - "is-ci": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", - "dev": true, - "requires": { - "ci-info": "^1.5.0" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-generator-function": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.7.tgz", - "integrity": "sha512-YZc5EwyO4f2kWCax7oegfuSr9mFz1ZvieNYBEjmukLxgXfBUbxAWGVF7GZf0zidYtoBl3WvC07YK0wT76a+Rtw==" - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", - "dev": true, - "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" - } - }, - "is-npm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", - "dev": true - }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" - }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", - "dev": true - }, - "is-regex": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", - "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", - "requires": { - "has": "^1.0.1" - } - }, - "is-retry-allowed": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", - "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "requires": { - "has-symbols": "^1.0.0" - } - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "iterall": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz", - "integrity": "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==" - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "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": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "keygrip": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.3.tgz", - "integrity": "sha512-/PpesirAIfaklxUzp4Yb7xBper9MwP6hNRA6BGGUFCgbJ+BM5CKBtsoxinNXkLHAr+GXS1/lSlF2rP7cv5Fl+g==" - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "koa": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/koa/-/koa-2.7.0.tgz", - "integrity": "sha512-7ojD05s2Q+hFudF8tDLZ1CpCdVZw8JQELWSkcfG9bdtoTDzMmkRF6BQBU7JzIzCCOY3xd3tftiy/loHBUYaY2Q==", - "requires": { - "accepts": "^1.3.5", - "cache-content-type": "^1.0.0", - "content-disposition": "~0.5.2", - "content-type": "^1.0.4", - "cookies": "~0.7.1", - "debug": "~3.1.0", - "delegates": "^1.0.0", - "depd": "^1.1.2", - "destroy": "^1.0.4", - "error-inject": "^1.0.0", - "escape-html": "^1.0.3", - "fresh": "~0.5.2", - "http-assert": "^1.3.0", - "http-errors": "^1.6.3", - "is-generator-function": "^1.0.7", - "koa-compose": "^4.1.0", - "koa-convert": "^1.2.0", - "koa-is-json": "^1.0.0", - "on-finished": "^2.3.0", - "only": "~0.0.2", - "parseurl": "^1.3.2", - "statuses": "^1.5.0", - "type-is": "^1.6.16", - "vary": "^1.1.2" - } - }, - "koa-bodyparser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/koa-bodyparser/-/koa-bodyparser-3.2.0.tgz", - "integrity": "sha1-uRbeF+IDn+gmUEgZc9fClPELVxk=", - "requires": { - "co-body": "^4.2.0" - } - }, - "koa-compose": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", - "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==" - }, - "koa-convert": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-1.2.0.tgz", - "integrity": "sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA=", - "requires": { - "co": "^4.6.0", - "koa-compose": "^3.0.0" - }, - "dependencies": { - "koa-compose": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-3.2.1.tgz", - "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=", - "requires": { - "any-promise": "^1.1.0" - } - } - } - }, - "koa-is-json": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/koa-is-json/-/koa-is-json-1.0.0.tgz", - "integrity": "sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ=" - }, - "koa-router": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/koa-router/-/koa-router-7.4.0.tgz", - "integrity": "sha512-IWhaDXeAnfDBEpWS6hkGdZ1ablgr6Q6pGdXCyK38RbzuH4LkUOpPqPw+3f8l8aTDrQmBQ7xJc0bs2yV4dzcO+g==", - "requires": { - "debug": "^3.1.0", - "http-errors": "^1.3.1", - "koa-compose": "^3.0.0", - "methods": "^1.0.1", - "path-to-regexp": "^1.1.1", - "urijs": "^1.19.0" - }, - "dependencies": { - "koa-compose": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-3.2.1.tgz", - "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=", - "requires": { - "any-promise": "^1.1.0" - } - } - } - }, - "latest-version": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", - "dev": true, - "requires": { - "package-json": "^4.0.0" - } - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "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": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" - }, - "long": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", - "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" - }, - "lowdb": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz", - "integrity": "sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==", - "requires": { - "graceful-fs": "^4.1.3", - "is-promise": "^2.1.0", - "lodash": "4", - "pify": "^3.0.0", - "steno": "^0.4.1" - } - }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" - }, - "methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "mime-db": { - "version": "1.38.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.38.0.tgz", - "integrity": "sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg==" - }, - "mime-types": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.22.tgz", - "integrity": "sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog==", - "requires": { - "mime-db": "~1.38.0" - } - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, - "nan": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.2.tgz", - "integrity": "sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==", - "dev": true, - "optional": true - }, - "nanoid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.0.1.tgz", - "integrity": "sha512-k1u2uemjIGsn25zmujKnotgniC/gxQ9sdegdezeDiKdkDW56THUMqlz3urndKCXJxA6yPzSZbXx/QCMe/pxqsA==" - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "negotiator": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", - "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" - }, - "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-fetch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz", - "integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA==" - }, - "nodemon": { - "version": "1.18.11", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.18.11.tgz", - "integrity": "sha512-KdN3tm1zkarlqNo4+W9raU3ihM4H15MVMSE/f9rYDZmFgDHAfAJsomYrHhApAkuUemYjFyEeXlpCOQ2v5gtBEw==", - "dev": true, - "requires": { - "chokidar": "^2.1.5", - "debug": "^3.1.0", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.6", - "semver": "^5.5.0", - "supports-color": "^5.2.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.2", - "update-notifier": "^2.5.0" - } - }, - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", - "dev": true, - "requires": { - "abbrev": "1" - } - }, - "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" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - }, - "object-path": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.4.tgz", - "integrity": "sha1-NwrnUvvzfePqcKhhwju6iRVpGUk=" - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, - "object.getownpropertydescriptors": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", - "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", - "requires": { - "define-properties": "^1.1.2", - "es-abstract": "^1.5.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", - "requires": { - "ee-first": "1.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "only": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", - "integrity": "sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q=" - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", - "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", - "dev": true, - "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" - } - }, - "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": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "dev": true, - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "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": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", - "requires": { - "isarray": "0.0.1" - } - }, - "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" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - } - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, - "prettier": { - "version": "1.16.4", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.4.tgz", - "integrity": "sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==", - "dev": true - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dev": true, - "requires": { - "fast-diff": "^1.1.2" - } - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "promises-all": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/promises-all/-/promises-all-1.0.0.tgz", - "integrity": "sha1-pDGMuNRWifZzkE4hVg8DI5cCgg8=", - "requires": { - "bluebird": "^3.4.7" - } - }, - "protobufjs": { - "version": "6.8.8", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", - "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", - "requires": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.4", - "@protobufjs/eventemitter": "^1.1.0", - "@protobufjs/fetch": "^1.1.0", - "@protobufjs/float": "^1.0.2", - "@protobufjs/inquire": "^1.1.0", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.0", - "@types/long": "^4.0.0", - "@types/node": "^10.1.0", - "long": "^4.0.0" - }, - "dependencies": { - "@types/node": { - "version": "10.14.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.4.tgz", - "integrity": "sha512-DT25xX/YgyPKiHFOpNuANIQIVvYEwCWXgK2jYYwqgaMrYE6+tq+DtmMwlD3drl6DJbUwtlIDnn0d7tIn/EbXBg==" - } - } - }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", - "dev": true - }, - "pstree.remy": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.6.tgz", - "integrity": "sha512-NdF35+QsqD7EgNEI5mkI/X+UwaxVEbQaz9f4IooEmMUv6ZPmlTQYGjBPJGgrlzNdjSvIy4MWMg6Q6vCgBO2K+w==", - "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 - }, - "qs": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-4.0.0.tgz", - "integrity": "sha1-wx2bdOwn33XlQ6hseHKO2NRiNgc=" - }, - "raw-body": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.7.tgz", - "integrity": "sha1-rf6s4uT7MJgFgBTQjActzFl1h3Q=", - "requires": { - "bytes": "2.4.0", - "iconv-lite": "0.4.13", - "unpipe": "1.0.0" - } - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "dev": true, - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "dev": true, - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - } - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", - "dev": true - }, - "registry-auth-token": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", - "dev": true, - "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" - } - }, - "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", - "dev": true, - "requires": { - "rc": "^1.0.1" - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "resolve": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", - "integrity": "sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "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 - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "rxjs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", - "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true - }, - "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", - "dev": true, - "requires": { - "semver": "^5.0.3" - } - }, - "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "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": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shortid": { - "version": "2.2.14", - "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.14.tgz", - "integrity": "sha512-4UnZgr9gDdA1kaKj/38IiudfC3KHKhDc1zi/HSxd9FQDR0VLwH3/y79tZJLsVYPsJgIjeHjqIWaWVRJUj9qZOQ==", - "requires": { - "nanoid": "^2.0.0" - } - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - } - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "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" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", - "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==", - "dev": true - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" - }, - "steno": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/steno/-/steno-0.4.4.tgz", - "integrity": "sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs=", - "requires": { - "graceful-fs": "^4.1.3" - } - }, - "streamsearch": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-0.1.2.tgz", - "integrity": "sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo=" - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "subscriptions-transport-ws": { - "version": "0.9.16", - "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.16.tgz", - "integrity": "sha512-pQdoU7nC+EpStXnCfh/+ho0zE0Z+ma+i7xvj7bkXKb1dvYHSZxgRPaU6spRP+Bjzow67c/rRDoix5RT0uU9omw==", - "requires": { - "backo2": "^1.0.2", - "eventemitter3": "^3.1.0", - "iterall": "^1.2.1", - "symbol-observable": "^1.0.4", - "ws": "^5.2.0" - }, - "dependencies": { - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "requires": { - "async-limiter": "~1.0.0" - } - } - } - }, - "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" - } - }, - "symbol-observable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", - "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" - }, - "table": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz", - "integrity": "sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ==", - "dev": true, - "requires": { - "ajv": "^6.9.1", - "lodash": "^4.17.11", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "term-size": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", - "dev": true, - "requires": { - "execa": "^0.7.0" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, - "toidentifier": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", - "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" - }, - "touch": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", - "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", - "dev": true, - "requires": { - "nopt": "~1.0.10" - } - }, - "ts-invariant": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.3.3.tgz", - "integrity": "sha512-UReOKsrJFGC9tUblgSRWo+BsVNbEd77Cl6WiV/XpMlkifXwNIJbknViCucHvVZkXSC/mcWeRnIGdY7uprcwvdQ==", - "requires": { - "tslib": "^1.9.3" - } - }, - "tslib": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", - "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "type-is": { - "version": "1.6.16", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.18" - } - }, - "undefsafe": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz", - "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=", - "dev": true, - "requires": { - "debug": "^2.2.0" - }, - "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" - } - } - } - }, - "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } - } - }, - "unique-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", - "dev": true, - "requires": { - "crypto-random-string": "^1.0.0" - } - }, - "unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - } - } - }, - "unzip-response": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", - "dev": true - }, - "upath": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", - "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", - "dev": true - }, - "update-notifier": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", - "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", - "dev": true, - "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "urijs": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.1.tgz", - "integrity": "sha512-xVrGVi94ueCJNrBSTjWqjvtgvl3cyOTThp2zaMaFNGp3F542TR6sM3f2o8RqZl+AwteClSVmoCyt0ka4RjQOQg==" - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "dev": true, - "requires": { - "prepend-http": "^1.0.1" - } - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "util.promisify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", - "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", - "requires": { - "define-properties": "^1.1.2", - "object.getownpropertydescriptors": "^2.0.3" - } - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" - }, - "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" - } - }, - "vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" - }, - "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" - } - }, - "widest-line": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", - "dev": true, - "requires": { - "string-width": "^2.1.1" - } - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, - "write-file-atomic": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz", - "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, - "ws": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", - "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", - "requires": { - "async-limiter": "~1.0.0" - } - }, - "xdg-basedir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", - "dev": true - }, - "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" - }, - "ylru": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.2.1.tgz", - "integrity": "sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ==" - }, - "zen-observable": { - "version": "0.8.14", - "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.14.tgz", - "integrity": "sha512-kQz39uonEjEESwh+qCi83kcC3rZJGh4mrZW7xjkSQYXkq//JZHTtKo+6yuVloTgMtzsIWOJrjIrKvk/dqm0L5g==" - }, - "zen-observable-ts": { - "version": "0.8.18", - "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.18.tgz", - "integrity": "sha512-q7d05s75Rn1j39U5Oapg3HI2wzriVwERVo4N7uFGpIYuHB9ff02P/E92P9B8T7QVC93jCMHpbXH7X0eVR5LA7A==", - "requires": { - "tslib": "^1.9.3", - "zen-observable": "^0.8.0" - } - } - } -} diff --git a/samples/client-provider/file-upload/server/package.json b/samples/client-provider/file-upload/server/package.json deleted file mode 100644 index 94f6689b2..000000000 --- a/samples/client-provider/file-upload/server/package.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "name": "apollo-upload-examples-api", - "private": true, - "license": "MIT", - "author": { - "name": "Jayden Seric", - "email": "me@jaydenseric.com", - "url": "https://jaydenseric.com" - }, - "engines": { - "node": ">=8.6" - }, - "dependencies": { - "apollo-server-koa": "^2.4.8", - "dotenv": "^6.2.0", - "graphql": "^14.2.1", - "koa": "^2.7.0", - "lowdb": "^1.0.0", - "mkdirp": "^0.5.1", - "promises-all": "^1.0.0", - "shortid": "^2.2.14" - }, - "devDependencies": { - "eslint": "^5.16.0", - "eslint-config-env": "^4.0.0", - "eslint-config-prettier": "^3.6.0", - "eslint-plugin-import": "^2.16.0", - "eslint-plugin-import-order-alphabetical": "^0.0.2", - "eslint-plugin-node": "^8.0.1", - "eslint-plugin-prettier": "^3.0.1", - "nodemon": "^1.18.11", - "prettier": "^1.16.4" - }, - "scripts": { - "dev": "nodemon -i db.json", - "start": "node --experimental-modules -r dotenv/config server", - "test": "eslint . --ext mjs,js && prettier '**/*.{json,yml,md}' -l" - }, - "eslintConfig": { - "extends": [ - "env" - ], - "rules": { - "require-jsdoc": "off" - } - }, - "prettier": { - "proseWrap": "never", - "singleQuote": true, - "semi": false - } -} diff --git a/samples/client-provider/file-upload/server/resolvers.mjs b/samples/client-provider/file-upload/server/resolvers.mjs deleted file mode 100644 index b2887c342..000000000 --- a/samples/client-provider/file-upload/server/resolvers.mjs +++ /dev/null @@ -1,70 +0,0 @@ -import fs from 'fs' -import apolloServerKoa from 'apollo-server-koa' -import lowdb from 'lowdb' -import FileSync from 'lowdb/adapters/FileSync' -import mkdirp from 'mkdirp' -import promisesAll from 'promises-all' -import shortid from 'shortid' - -const UPLOAD_DIR = './uploads' -const db = lowdb(new FileSync('db.json')) - -// Seed an empty DB. -db.defaults({ uploads: [] }).write() - -// Ensure upload directory exists. -mkdirp.sync(UPLOAD_DIR) - -const storeFS = ({ stream, filename }) => { - const id = shortid.generate() - const path = `${UPLOAD_DIR}/${id}-${filename}` - return new Promise((resolve, reject) => - stream - .on('error', error => { - if (stream.truncated) - // Delete the truncated file. - fs.unlinkSync(path) - reject(error) - }) - .pipe(fs.createWriteStream(path)) - .on('error', error => reject(error)) - .on('finish', () => resolve({ id, path })) - ) -} - -const storeDB = file => - db - .get('uploads') - .push(file) - .last() - .write() - -const processUpload = async upload => { - const { createReadStream, filename, mimetype } = await upload - const stream = createReadStream() - const { id, path } = await storeFS({ stream, filename }) - return storeDB({ id, filename, mimetype, path }) -} - -export default { - Upload: apolloServerKoa.GraphQLUpload, - Query: { - uploads: () => db.get('uploads').value() - }, - Mutation: { - singleUpload: (obj, { request }) => processUpload(request.file), - async multipleUpload(obj, { request }) { - const { resolve, reject } = await promisesAll.all( - request.files.map(processUpload) - ) - - if (reject.length) - reject.forEach(({ name, message }) => - // eslint-disable-next-line no-console - console.error(`${name}: ${message}`) - ) - - return resolve - } - } -} diff --git a/samples/client-provider/file-upload/server/server.mjs b/samples/client-provider/file-upload/server/server.mjs deleted file mode 100644 index e2f629f9d..000000000 --- a/samples/client-provider/file-upload/server/server.mjs +++ /dev/null @@ -1,29 +0,0 @@ -import apolloServerKoa from 'apollo-server-koa' -import Koa from 'koa' -import resolvers from './resolvers' -import typeDefs from './types' - -const app = new Koa() -const server = new apolloServerKoa.ApolloServer({ - typeDefs, - resolvers, - uploads: { - // Limits here should be stricter than config for surrounding - // infrastructure such as Nginx so errors can be handled elegantly by - // graphql-upload: - // https://github.com/jaydenseric/graphql-upload#type-uploadoptions - maxFileSize: 10000000, // 10 MB - maxFiles: 20 - } -}) - -server.applyMiddleware({ app }) - -app.listen(process.env.PORT, error => { - if (error) throw error - - // eslint-disable-next-line no-console - console.info( - `Serving http://localhost:${process.env.PORT} for ${process.env.NODE_ENV}.` - ) -}) diff --git a/samples/client-provider/file-upload/server/types.mjs b/samples/client-provider/file-upload/server/types.mjs deleted file mode 100644 index 2fc0375cd..000000000 --- a/samples/client-provider/file-upload/server/types.mjs +++ /dev/null @@ -1,25 +0,0 @@ -export default /* GraphQL */ ` - type File { - id: ID! - path: String! - filename: String! - mimetype: String! - } - - type Query { - uploads: [File] - } - - input SingleUploadRequest { - file: Upload! - } - - input MultipleUploadRequest { - files: [Upload!]! - } - - type Mutation { - singleUpload(request: SingleUploadRequest!): File! - multipleUpload(request: MultipleUploadRequest!): [File!]! - } -` diff --git a/samples/client-provider/file-upload/single_file_upload.fsx b/samples/client-provider/file-upload/single_file_upload.fsx deleted file mode 100644 index 4cc0fe925..000000000 --- a/samples/client-provider/file-upload/single_file_upload.fsx +++ /dev/null @@ -1,40 +0,0 @@ -// Uncomment those to use build script client assembly -//#r "../../../bin/FSharp.Data.GraphQL.Client/net47/FSharp.Data.GraphQL.Client.dll" -//#r "../../../bin/FSharp.Data.GraphQL.Shared/net47/FSharp.Data.GraphQL.Shared.dll" - -// Uncomment those to use build script client assembly using netstandard2.0 -//#r "../../../bin/FSharp.Data.GraphQL.Shared/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -//#r "../../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/netstandard.dll" -//#r "../../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/FSharp.Data.GraphQL.Client.dll" - -// Uncomment those to use dotnet build command for the client assembly -// #r "../../../src/FSharp.Data.GraphQL.Shared/bin/Debug/net47/FSharp.Data.GraphQL.Shared.dll" -// #r "../../../src/FSharp.Data.GraphQL.Client/bin/Debug/net47/FSharp.Data.GraphQL.Client.dll" - -//Uncomment those to use dotnet build command for the client assembly using netstandard2.0 -#r "../../../src/FSharp.Data.GraphQL.Shared/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -#r "../../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/netstandard.dll" -#r "../../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Client.dll" - -open System.IO -open FSharp.Data.GraphQL - -type MyProvider = GraphQLProvider<"http://localhost:3001/graphql", uploadInputTypeName = "Upload"> - -let mutation = - MyProvider.Operation<"""mutation singleFileUpload($request : SingleUploadRequest!) { - singleUpload(request : $request) { - id - path - filename - mimetype - } - }""">() - -let upload() = - use upload = new Upload(File.OpenRead("txt_file.txt"), "text.txt", ownsStream = true) - let input = MyProvider.Types.SingleUploadRequest(upload) - let result = mutation.Run(input) - printfn "Data: %A" result.Data - -upload() \ No newline at end of file diff --git a/samples/client-provider/file-upload/uploaded_files.fsx b/samples/client-provider/file-upload/uploaded_files.fsx deleted file mode 100644 index 1a2280500..000000000 --- a/samples/client-provider/file-upload/uploaded_files.fsx +++ /dev/null @@ -1,36 +0,0 @@ -// Uncomment those to use build script client assembly -//#r "../../../bin/FSharp.Data.GraphQL.Client/net47/FSharp.Data.GraphQL.Client.dll" -//#r "../../../bin/FSharp.Data.GraphQL.Shared/net47/FSharp.Data.GraphQL.Shared.dll" - -// Uncomment those to use build script client assembly using netstandard2.0 -//#r "../../../bin/FSharp.Data.GraphQL.Shared/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -//#r "../../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/netstandard.dll" -//#r "../../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/FSharp.Data.GraphQL.Client.dll" - -// Uncomment those to use dotnet build command for the client assembly -// #r "../../../src/FSharp.Data.GraphQL.Shared/bin/Debug/net47/FSharp.Data.GraphQL.Shared.dll" -// #r "../../../src/FSharp.Data.GraphQL.Client/bin/Debug/net47/FSharp.Data.GraphQL.Client.dll" - -//Uncomment those to use dotnet build command for the client assembly using netstandard2.0 -#r "../../../src/FSharp.Data.GraphQL.Shared/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -#r "../../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/netstandard.dll" -#r "../../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Client.dll" - -open System.IO -open FSharp.Data.GraphQL - -type MyProvider = GraphQLProvider<"http://localhost:3001/graphql", uploadInputTypeName = "Upload"> - -let query = - MyProvider.Operation<"""query uploadsQuery { - uploads { - id - filename - mimetype - path - } - }""">() - -let result = query.Run() - -printfn "Data: %A" result.Data \ No newline at end of file diff --git a/samples/client-provider/fragments.fsx b/samples/client-provider/fragments.fsx new file mode 100644 index 000000000..ecbf4cfae --- /dev/null +++ b/samples/client-provider/fragments.fsx @@ -0,0 +1,56 @@ +#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/typeproviders/fsharp41/netstandard2.0/Microsoft.Extensions.Http.dll" +#r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" +#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Client.dll" + +open FSharp.Data.GraphQL + +let [] fragmentsFolder = __SOURCE_DIRECTORY__ + "/fragments" +type MyProvider = GraphQLProvider<"swapi_schema.json", fragmentsFolder="fragments", resolutionFolder=__SOURCE_DIRECTORY__> + +(*** +type HeroFragment = MyProvider.Fragments.HeroFragment +type Droid = MyProvider.Fragments.CharacterFragment.Droid +**) + +let operation = + MyProvider.Operation<"""query TestQuery { + myHero: hero(id: "1000") { + hisName: name + appearsIn + homePlanet + hisFriends: friends { + ... on Human { + humanName: name + appearsIn + homePlanet + } + ... on Droid { + droidName: name + appearsIn + primaryFunction + } + } + } + }""">() + +let ctx = MyProvider.GetContext(serverUrl = "http://localhost:8086") + +let result = operation.Run(ctx) + +let hisName = result.Data.Value.MyHero.Value.HisName.Value + +let hisFriends = result.Data.Value.MyHero.Value.HisFriends |> Array.choose id + +let humanFriendNames = + hisFriends |> Array.choose (fun f -> f.TryAsHuman()) |> Array.map (fun h -> h.HumanName) + +let droidFriendNames = + hisFriends |> Array.choose (fun f -> f.TryAsDroid()) |> Array.map (fun h -> h.DroidName) + +printfn "His name: %s" hisName + +printfn "Human friend names: %A" humanFriendNames + +printfn "Droid friend names: %A" droidFriendNames + +printfn "Data: %A" result.Data \ No newline at end of file diff --git a/samples/client-provider/fragments/character.graphql b/samples/client-provider/fragments/character.graphql new file mode 100644 index 000000000..63cd2977f --- /dev/null +++ b/samples/client-provider/fragments/character.graphql @@ -0,0 +1,12 @@ +fragment CharacterFragment on Character { + ... on Human { + humanName: name + appearsIn + homePlanet + } + ... on Droid { + droidName: name + appearsIn + primaryFunction + } +} \ No newline at end of file diff --git a/samples/client-provider/fragments/hero.graphql b/samples/client-provider/fragments/hero.graphql new file mode 100644 index 000000000..65101b805 --- /dev/null +++ b/samples/client-provider/fragments/hero.graphql @@ -0,0 +1,5 @@ +fragment HeroFragment on Human { + hisName: name + appearsIn + homePlanet +} \ No newline at end of file diff --git a/samples/client-provider/github_access.fsx b/samples/client-provider/github_access.fsx index 44e50aea6..c67326104 100644 --- a/samples/client-provider/github_access.fsx +++ b/samples/client-provider/github_access.fsx @@ -1,19 +1,5 @@ -// Uncomment those to use build script client assembly -//#r "../../bin/FSharp.Data.GraphQL.Client/net47/FSharp.Data.GraphQL.Client.dll" -//#r "../../bin/FSharp.Data.GraphQL.Shared/net47/FSharp.Data.GraphQL.Shared.dll" - -// Uncomment those to use build script client assembly using netstandard2.0 -//#r "../../bin/FSharp.Data.GraphQL.Shared/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/netstandard.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/FSharp.Data.GraphQL.Client.dll" - -// Uncomment those to use dotnet build command for the client assembly -// #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/net47/FSharp.Data.GraphQL.Shared.dll" -// #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/net47/FSharp.Data.GraphQL.Client.dll" - -//Uncomment those to use dotnet build command for the client assembly using netstandard2.0 +#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/typeproviders/fsharp41/netstandard2.0/Microsoft.Extensions.Http.dll" #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/netstandard.dll" #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Client.dll" open FSharp.Data.GraphQL @@ -21,11 +7,12 @@ open FSharp.Data.GraphQL // Some GraphQL API's gives access to their schema via GET method, whithout need to anthenticate via headers. // The provider automatically tries to get the schema via GET method first. If it does not work, // The classical way via POST is done. -type MyProvider = GraphQLProvider<"github_schema.json"> +let [] Schema = __SOURCE_DIRECTORY__ + "/github_schema.json" +type MyProvider = GraphQLProvider let operation = MyProvider.Operation<"""query q { viewer { login } }""">() -let headers = HttpHeaders.ofFile "github_authorization_headers.headerfile" +let headers = HttpHeaders.ofFile (__SOURCE_DIRECTORY__ + "/github_authorization_headers.headerfile") let run () = // Dispose runtime context after using it. diff --git a/samples/client-provider/graphql_client.fsx b/samples/client-provider/graphql_client.fsx index ce44b8dc0..47fea0727 100644 --- a/samples/client-provider/graphql_client.fsx +++ b/samples/client-provider/graphql_client.fsx @@ -1,19 +1,5 @@ -// Uncomment those to use build script client assembly -//#r "../../bin/FSharp.Data.GraphQL.Client/net47/FSharp.Data.GraphQL.Client.dll" -//#r "../../bin/FSharp.Data.GraphQL.Shared/net47/FSharp.Data.GraphQL.Shared.dll" - -// Uncomment those to use build script client assembly using netstandard2.0 -//#r "../../bin/FSharp.Data.GraphQL.Shared/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/netstandard.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/FSharp.Data.GraphQL.Client.dll" - -// Uncomment those to use dotnet build command for the client assembly -// #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/net47/FSharp.Data.GraphQL.Shared.dll" -// #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/net47/FSharp.Data.GraphQL.Client.dll" - -//Uncomment those to use dotnet build command for the client assembly using netstandard2.0 +#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/typeproviders/fsharp41/netstandard2.0/Microsoft.Extensions.Http.dll" #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/netstandard.dll" #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Client.dll" open FSharp.Data.GraphQL @@ -25,7 +11,7 @@ let run () = { Query = """query q { viewer { login } }""" Variables = [||] ServerUrl = "https://api.github.com/graphql" - HttpHeaders = + HttpHeaders = [| "Authorization", "bearer [your bearer token here]" "User-Agent", "[your github username here]" |] OperationName = Some "q" } diff --git a/samples/client-provider/http_headers_introspection.fsx b/samples/client-provider/http_headers_introspection.fsx index 27950e45c..c23a815d8 100644 --- a/samples/client-provider/http_headers_introspection.fsx +++ b/samples/client-provider/http_headers_introspection.fsx @@ -1,19 +1,5 @@ -// Uncomment those to use build script client assembly -//#r "../../bin/FSharp.Data.GraphQL.Client/net47/FSharp.Data.GraphQL.Client.dll" -//#r "../../bin/FSharp.Data.GraphQL.Shared/net47/FSharp.Data.GraphQL.Shared.dll" - -// Uncomment those to use build script client assembly using netstandard2.0 -//#r "../../bin/FSharp.Data.GraphQL.Shared/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/netstandard.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/FSharp.Data.GraphQL.Client.dll" - -// Uncomment those to use dotnet build command for the client assembly -// #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/net47/FSharp.Data.GraphQL.Shared.dll" -// #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/net47/FSharp.Data.GraphQL.Client.dll" - -//Uncomment those to use dotnet build command for the client assembly using netstandard2.0 +#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/typeproviders/fsharp41/netstandard2.0/Microsoft.Extensions.Http.dll" #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/netstandard.dll" #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Client.dll" open FSharp.Data.GraphQL @@ -26,7 +12,7 @@ open FSharp.Data.GraphQL //type MyProvider = GraphQLProvider<"http://localhost:8086", "http_headers1.headerfile"> type MyProvider = GraphQLProvider<"http://localhost:8086", "UserData: 45883115-db2f-4ccc-ae6f-21ec17d4a7a1"> -let operation = +let operation = MyProvider.Operation<"""query q { hero (id: "1000") { name diff --git a/samples/client-provider/mutations.fsx b/samples/client-provider/mutations.fsx index afe59bf5d..2ed6e3163 100644 --- a/samples/client-provider/mutations.fsx +++ b/samples/client-provider/mutations.fsx @@ -1,26 +1,12 @@ -// Uncomment those to use build script client assembly -//#r "../../bin/FSharp.Data.GraphQL.Client/net47/FSharp.Data.GraphQL.Client.dll" -//#r "../../bin/FSharp.Data.GraphQL.Shared/net47/FSharp.Data.GraphQL.Shared.dll" - -// Uncomment those to use build script client assembly using netstandard2.0 -//#r "../../bin/FSharp.Data.GraphQL.Shared/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/netstandard.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/FSharp.Data.GraphQL.Client.dll" - -// Uncomment those to use dotnet build command for the client assembly -// #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/net47/FSharp.Data.GraphQL.Shared.dll" -// #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/net47/FSharp.Data.GraphQL.Client.dll" - -//Uncomment those to use dotnet build command for the client assembly using netstandard2.0 +#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/typeproviders/fsharp41/netstandard2.0/Microsoft.Extensions.Http.dll" #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/netstandard.dll" #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Client.dll" open FSharp.Data.GraphQL type MyProvider = GraphQLProvider<"http://localhost:8086"> -let operation = +let operation = MyProvider.Operation<"""mutation m { setMoon (id: "1", isMoon: true) { id diff --git a/samples/client-provider/parameters.fsx b/samples/client-provider/parameters.fsx index 27a1ede53..86b94cd55 100644 --- a/samples/client-provider/parameters.fsx +++ b/samples/client-provider/parameters.fsx @@ -1,26 +1,12 @@ -// Uncomment those to use build script client assembly -//#r "../../bin/FSharp.Data.GraphQL.Client/net47/FSharp.Data.GraphQL.Client.dll" -//#r "../../bin/FSharp.Data.GraphQL.Shared/net47/FSharp.Data.GraphQL.Shared.dll" - -// Uncomment those to use build script client assembly using netstandard2.0 -//#r "../../bin/FSharp.Data.GraphQL.Shared/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/netstandard.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/FSharp.Data.GraphQL.Client.dll" - -// Uncomment those to use dotnet build command for the client assembly -// #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/net47/FSharp.Data.GraphQL.Shared.dll" -// #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/net47/FSharp.Data.GraphQL.Client.dll" - -//Uncomment those to use dotnet build command for the client assembly using netstandard2.0 +#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/typeproviders/fsharp41/netstandard2.0/Microsoft.Extensions.Http.dll" #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/netstandard.dll" #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Client.dll" open FSharp.Data.GraphQL type MyProvider = GraphQLProvider<"http://localhost:8086"> -let operation = +let operation = MyProvider.Operation<"""query q($id: String!) { hero(id: $id) { name diff --git a/samples/client-provider/short_hand_query.fsx b/samples/client-provider/short_hand_query.fsx index 5614ff991..68e0f2a7e 100644 --- a/samples/client-provider/short_hand_query.fsx +++ b/samples/client-provider/short_hand_query.fsx @@ -1,26 +1,12 @@ -// Uncomment those to use build script client assembly -//#r "../../bin/FSharp.Data.GraphQL.Client/net47/FSharp.Data.GraphQL.Client.dll" -//#r "../../bin/FSharp.Data.GraphQL.Shared/net47/FSharp.Data.GraphQL.Shared.dll" - -// Uncomment those to use build script client assembly using netstandard2.0 -//#r "../../bin/FSharp.Data.GraphQL.Shared/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/netstandard.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/FSharp.Data.GraphQL.Client.dll" - -// Uncomment those to use dotnet build command for the client assembly -// #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/net47/FSharp.Data.GraphQL.Shared.dll" -// #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/net47/FSharp.Data.GraphQL.Client.dll" - -//Uncomment those to use dotnet build command for the client assembly using netstandard2.0 +#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/typeproviders/fsharp41/netstandard2.0/Microsoft.Extensions.Http.dll" #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/netstandard.dll" #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Client.dll" open FSharp.Data.GraphQL type MyProvider = GraphQLProvider<"swapi_schema.json"> -let operation = +let operation = MyProvider.Operation<"""{ hero(id: "1000") { name diff --git a/samples/client-provider/standard_features.fsx b/samples/client-provider/standard_features.fsx index 381fe9476..83e052dfa 100644 --- a/samples/client-provider/standard_features.fsx +++ b/samples/client-provider/standard_features.fsx @@ -1,19 +1,5 @@ -// Uncomment those to use build script client assembly -//#r "../../bin/FSharp.Data.GraphQL.Client/net461/FSharp.Data.GraphQL.Client.dll" -//#r "../../bin/FSharp.Data.GraphQL.Shared/net461/FSharp.Data.GraphQL.Shared.dll" - -// Uncomment those to use build script client assembly using netstandard2.0 -//#r "../../bin/FSharp.Data.GraphQL.Shared/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/netstandard.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/FSharp.Data.GraphQL.Client.dll" - -// Uncomment those to use dotnet build command for the client assembly -// #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/net461/FSharp.Data.GraphQL.Shared.dll" -// #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/net461/FSharp.Data.GraphQL.Client.dll" - -//Uncomment those to use dotnet build command for the client assembly using netstandard2.0 +#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/typeproviders/fsharp41/netstandard2.0/Microsoft.Extensions.Http.dll" #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/netstandard.dll" #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Client.dll" open FSharp.Data.GraphQL @@ -32,7 +18,7 @@ type MyProvider = GraphQLProvider<"http://localhost:8086"> // The operation method can be used to make queries, mutations, and subscriptions. // Although subscription operations can be created, the client provider still // does not work with web sockets - only the immediate response will be known. -let operation = +let operation = MyProvider.Operation<"""query q { hero (id: "1000") { name diff --git a/samples/client-provider/using_query_file.fsx b/samples/client-provider/using_query_file.fsx index 5c4005cf8..baf3f289e 100644 --- a/samples/client-provider/using_query_file.fsx +++ b/samples/client-provider/using_query_file.fsx @@ -1,19 +1,5 @@ -// Uncomment those to use build script client assembly -//#r "../../bin/FSharp.Data.GraphQL.Client/net47/FSharp.Data.GraphQL.Client.dll" -//#r "../../bin/FSharp.Data.GraphQL.Shared/net47/FSharp.Data.GraphQL.Shared.dll" - -// Uncomment those to use build script client assembly using netstandard2.0 -//#r "../../bin/FSharp.Data.GraphQL.Shared/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/netstandard.dll" -//#r "../../bin/FSharp.Data.GraphQL.Client/netstandard2.0/FSharp.Data.GraphQL.Client.dll" - -// Uncomment those to use dotnet build command for the client assembly -// #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/net47/FSharp.Data.GraphQL.Shared.dll" -// #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/net47/FSharp.Data.GraphQL.Client.dll" - -//Uncomment those to use dotnet build command for the client assembly using netstandard2.0 +#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/typeproviders/fsharp41/netstandard2.0/Microsoft.Extensions.Http.dll" #r "../../src/FSharp.Data.GraphQL.Shared/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" -#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/netstandard.dll" #r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Client.dll" open FSharp.Data.GraphQL diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/ApplicationInsights.config b/samples/file-uploads/ApplicationInsights.config similarity index 100% rename from tests/FSharp.Data.GraphQL.IntegrationTests.Server/ApplicationInsights.config rename to samples/file-uploads/ApplicationInsights.config diff --git a/samples/file-uploads/FSharp.Data.GraphQL.Samples.FileUpload.fsproj b/samples/file-uploads/FSharp.Data.GraphQL.Samples.FileUpload.fsproj new file mode 100644 index 000000000..d256f1b3a --- /dev/null +++ b/samples/file-uploads/FSharp.Data.GraphQL.Samples.FileUpload.fsproj @@ -0,0 +1,19 @@ + + + Exe + net5.0 + false + + + + + + + + + + + + + + diff --git a/samples/file-uploads/Program.fs b/samples/file-uploads/Program.fs new file mode 100644 index 000000000..3fd6aa299 --- /dev/null +++ b/samples/file-uploads/Program.fs @@ -0,0 +1,50 @@ +namespace FSharp.Data.GraphQL.Samples.FileUpload + +open Microsoft.AspNetCore +open Microsoft.AspNetCore.Hosting +open System +open Microsoft.AspNetCore +open Microsoft.AspNetCore.Http.Features +open Microsoft.AspNetCore.Builder +open Microsoft.AspNetCore.Hosting +open Microsoft.Extensions.Logging +open Microsoft.Extensions.DependencyInjection +open FSharp.Data.GraphQL +open FSharp.Data.GraphQL.Server +open FSharp.Data.GraphQL.Server.AspNet + +module Program = + let configureApp (app : IApplicationBuilder) = + let schema = Schema(Schema.Query, Schema.Mutation) + let executor = Executor(schema) + let buildRoot ctx = async { return () } + app.UseGraphQL(executor, buildRoot, path="/") |> ignore + + let configureServices (services : IServiceCollection) = + services.AddDataProtection() |> ignore + services.Configure(fun (formOptions:FormOptions) -> + formOptions.MultipartBodyLengthLimit <- 2_147_483_648L + formOptions.ValueLengthLimit <- Int32.MaxValue + formOptions.BufferBodyLengthLimit <- 2147483648L + ) |> ignore + + let configureLogging (loggerBuilder : ILoggingBuilder) = + loggerBuilder.AddFilter(fun lvl -> lvl.Equals LogLevel.Error) + .AddConsole() + .AddDebug() |> ignore + + [] + let main _ = + WebHost + .CreateDefaultBuilder() + .Configure(Action configureApp) + .ConfigureServices(configureServices) + .ConfigureLogging(configureLogging) + .UseKestrel(fun options -> + options.ListenAnyIP(8080) + options.Limits.MaxRequestBodySize <- Nullable 2147483648L + options.Limits.MaxRequestBufferSize <- Nullable 2147483648L + ) + .Build() + .Run() + 0 diff --git a/samples/file-uploads/Properties/launchSettings.json b/samples/file-uploads/Properties/launchSettings.json new file mode 100644 index 000000000..a794c5d5b --- /dev/null +++ b/samples/file-uploads/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:55802/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "FSharp.Data.GraphQL.Samples.FileUpload": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:55803/" + } + } +} \ No newline at end of file diff --git a/samples/file-uploads/Schema.fs b/samples/file-uploads/Schema.fs new file mode 100644 index 000000000..50278b1c8 --- /dev/null +++ b/samples/file-uploads/Schema.fs @@ -0,0 +1,75 @@ +namespace FSharp.Data.GraphQL.Samples.FileUpload + +open FSharp.Data.GraphQL.Types +open FSharp.Data.GraphQL.Server.AspNet +open type FSharp.Data.GraphQL.Types.SchemaDefinitions.Define + +type UploadsInput = + { Files : FileUpload list } + +type FileInfo = + { Hash: string + Path: string + FileName: string + ContentType: string } + +type Root = unit + +module Schema = + let mkFileInfo (upload: FileUpload) = + { Hash = upload.Hash + Path = upload.Path + FileName = upload.Name + ContentType = upload.ContentType } + + + let UploadsType = + InputObject( + name = "Uploads", + description = "The `Upload` scalar type represents a file upload.", + fields = [ + Input("files", ListOf FileUpload) + ] + ) + + let FileInfoType = + Object( + name = "FileInfo", + description = "The `FileInfo` type represents info about an upload", + fields = [ + AutoField("hash", String) + AutoField("path", String) + AutoField("fileName", String) + AutoField("contentType", String) + ] + ) + + let Query = + Object( + name = "Query", + fields = [ + Field("Message", String, "A greeting", fun ctx _ -> "Hello World") + ] + ) + + let Mutation = + Object( + name = "Mutation", + fields = [ + Field("singleUpload", FileInfoType, "upload a single file", [Input("file", FileUpload)], + fun ctx _ -> + let file = ctx.Arg("file") + mkFileInfo file + ) + Field("multiUpload", ListOf FileInfoType, "upload a single file", [Input("files", ListOf FileUpload)], + fun ctx _ -> + let files = ctx.Arg("files") + files |> List.map mkFileInfo + ) + Field("multiUploadRecord", ListOf FileInfoType, "upload a single file", [Input("files", UploadsType)], + fun ctx _ -> + let files = ctx.Arg("files") + files.Files |> List.map mkFileInfo + ) + ] + ) \ No newline at end of file diff --git a/samples/file-uploads/appsettings.json b/samples/file-uploads/appsettings.json new file mode 100644 index 000000000..5fff67bac --- /dev/null +++ b/samples/file-uploads/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "IncludeScopes": false, + "LogLevel": { + "Default": "Warning" + } + } +} diff --git a/samples/file-uploads/client-provider/multiple_files_upload.fsx b/samples/file-uploads/client-provider/multiple_files_upload.fsx new file mode 100644 index 000000000..ba57f32d0 --- /dev/null +++ b/samples/file-uploads/client-provider/multiple_files_upload.fsx @@ -0,0 +1,66 @@ +#r "../../../src/FSharp.Data.GraphQL.Client/bin/Debug/typeproviders/fsharp41/netstandard2.0/Microsoft.Extensions.Http.dll" +#r "../../../src/FSharp.Data.GraphQL.Shared/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Shared.dll" +#r "../../../src/FSharp.Data.GraphQL.Client/bin/Debug/netstandard2.0/FSharp.Data.GraphQL.Client.dll" + +open System +open System.IO +open FSharp.Data.GraphQL + +type MyProvider = GraphQLProvider<"http://localhost:8080/graphql", uploadInputTypeName = "Upload"> + +let multipleFiles = + MyProvider.Operation<"""mutation multipleFilesUpload($files: [Upload!]!) { + multiUpload(files: $files) { + hash + path + fileName + contentType + } + }""">() + +let singleFile = + MyProvider.Operation<"""mutation singleFilesUpload($file: Upload!) { + singleUpload(file: $file) { + hash + path + fileName + contentType + } + }""">() + + +let multiFileRecord = + MyProvider.Operation<"""mutation multiFileRecord($files: Uploads!) { + multiUploadRecord(files: $files) { + hash + path + fileName + contentType + } + }""">() + +let dir = __SOURCE_DIRECTORY__ + +let uploadSingle() = + let input = new Upload(File.OpenRead($"{dir}/txt_file.txt"), "text.txt", ownsStream = true) + let result = singleFile.Run(input) + printfn "Single Data: %A" result.Data + +let uploadMulti() = + let input = + [| new Upload(File.OpenRead($"{dir}/txt_file.txt"), "text.txt", ownsStream = true) + new Upload(File.OpenRead($"{dir}/png_file.png"), "image.png", ownsStream = true) |] + let result = multipleFiles.Run(input) + printfn "Multi Data: %A" result.Data + +let uploadMultiRecord() = + let input = + MyProvider.Types.Uploads([| + new Upload(File.OpenRead($"{dir}/txt_file.txt"), "text.txt", ownsStream = true) + new Upload(File.OpenRead($"{dir}/png_file.png"), "image.png", ownsStream = true) |]) + let result = multiFileRecord.Run(input) + printfn "Multi Record Data: %A" result.Data + +uploadSingle() +uploadMulti() +uploadMultiRecord() \ No newline at end of file diff --git a/samples/client-provider/file-upload/png_file.png b/samples/file-uploads/client-provider/png_file.png similarity index 100% rename from samples/client-provider/file-upload/png_file.png rename to samples/file-uploads/client-provider/png_file.png diff --git a/samples/client-provider/file-upload/txt_file.txt b/samples/file-uploads/client-provider/txt_file.txt similarity index 100% rename from samples/client-provider/file-upload/txt_file.txt rename to samples/file-uploads/client-provider/txt_file.txt diff --git a/samples/file-uploads/paket.references b/samples/file-uploads/paket.references new file mode 100644 index 000000000..d73ea7bd8 --- /dev/null +++ b/samples/file-uploads/paket.references @@ -0,0 +1,2 @@ +group TestsAndSamples +FSharp.Core \ No newline at end of file diff --git a/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj b/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj index 87cffddde..d256f1b3a 100644 --- a/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj +++ b/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj @@ -8,18 +8,12 @@ + - - - - - - - diff --git a/samples/star-wars-api/Helpers.fs b/samples/star-wars-api/Helpers.fs deleted file mode 100644 index d46f59ab9..000000000 --- a/samples/star-wars-api/Helpers.fs +++ /dev/null @@ -1,47 +0,0 @@ -namespace FSharp.Data.GraphQL.Samples.StarWarsApi - -open System -open System.Text -open Newtonsoft.Json -open Newtonsoft.Json.Linq -open Newtonsoft.Json.Serialization -open System.Collections.Generic - -[] -module Helpers = - let tee f x = - f x - x - -[] -module StringHelpers = - let utf8String (bytes : byte seq) = - bytes - |> Seq.filter (fun i -> i > 0uy) - |> Array.ofSeq - |> Encoding.UTF8.GetString - - let utf8Bytes (str : string) = - str |> Encoding.UTF8.GetBytes - - let isNullOrWhiteSpace (str : string) = - String.IsNullOrWhiteSpace(str) - -[] -module JsonHelpers = - let tryGetJsonProperty (jobj: JObject) prop = - match jobj.Property(prop) with - | null -> None - | p -> Some(p.Value.ToString()) - - let jsonSerializerSettings (converters : JsonConverter seq) = - JsonSerializerSettings() - |> tee (fun s -> - s.Converters <- List(converters) - s.ContractResolver <- CamelCasePropertyNamesContractResolver()) - - let jsonSerializer (converters : JsonConverter seq) = - JsonSerializer() - |> tee (fun c -> - Seq.iter c.Converters.Add converters - c.ContractResolver <- CamelCasePropertyNamesContractResolver()) diff --git a/samples/star-wars-api/HttpHandlers.fs b/samples/star-wars-api/HttpHandlers.fs deleted file mode 100644 index 916b27331..000000000 --- a/samples/star-wars-api/HttpHandlers.fs +++ /dev/null @@ -1,112 +0,0 @@ -namespace FSharp.Data.GraphQL.Samples.StarWarsApi - -open System.Text -open Giraffe -open Microsoft.AspNetCore.Http -open Newtonsoft.Json -open FSharp.Data.GraphQL.Execution -open System.IO -open FSharp.Data.GraphQL -open FSharp.Data.GraphQL.Types -open FSharp.Control.Tasks -open Newtonsoft.Json.Linq - -type HttpHandler = HttpFunc -> HttpContext -> HttpFuncResult - -module HttpHandlers = - let private converters : JsonConverter [] = [| OptionConverter() |] - let private jsonSettings = jsonSerializerSettings converters - - let internalServerError : HttpHandler = setStatusCode 500 - - let okWithStr str : HttpHandler = setStatusCode 200 >=> text str - - let setCorsHeaders : HttpHandler = - setHttpHeader "Access-Control-Allow-Origin" "*" - >=> setHttpHeader "Access-Control-Allow-Headers" "content-type" - - let setContentTypeAsJson : HttpHandler = - setHttpHeader "Content-Type" "application/json" - - let private graphQL (next : HttpFunc) (ctx : HttpContext) = task { - let serialize d = JsonConvert.SerializeObject(d, jsonSettings) - - let deserialize (data : string) = - let getMap (token : JToken) = - let rec mapper (name : string) (token : JToken) = - match name, token.Type with - | "variables", JTokenType.Object -> token.Children() |> Seq.map (fun x -> x.Name, mapper x.Name x.Value) |> Map.ofSeq |> box - | name, JTokenType.Array -> token |> Seq.map (fun x -> mapper name x) |> Array.ofSeq |> box - | _ -> (token :?> JValue).Value - token.Children() - |> Seq.map (fun x -> x.Name, mapper x.Name x.Value) - |> Map.ofSeq - if System.String.IsNullOrWhiteSpace(data) - then None - else data |> JToken.Parse |> getMap |> Some - - let json = - function - | Direct (data, _) -> - JsonConvert.SerializeObject(data, jsonSettings) - | Deferred (data, _, deferred) -> - deferred |> Observable.add(fun d -> printfn "Deferred: %s" (serialize d)) - JsonConvert.SerializeObject(data, jsonSettings) - | Stream data -> - data |> Observable.add(fun d -> printfn "Subscription data: %s" (serialize d)) - "{}" - - let removeWhitespacesAndLineBreaks (str : string) = str.Trim().Replace("\r\n", " ") - - let readStream (s : Stream) = - use ms = new MemoryStream(4096) - s.CopyTo(ms) - ms.ToArray() - - let data = Encoding.UTF8.GetString(readStream ctx.Request.Body) |> deserialize - - let query = - data |> Option.bind (fun data -> - if data.ContainsKey("query") - then - match data.["query"] with - | :? string as x -> Some x - | _ -> failwith "Failure deserializing repsonse. Could not read query - it is not stringified in request." - else None) - - let variables = - data |> Option.bind (fun data -> - if data.ContainsKey("variables") - then - match data.["variables"] with - | null -> None - | :? string as x -> deserialize x - | :? Map as x -> Some x - | _ -> failwith "Failure deserializing response. Could not read variables - it is not a object in the request." - else None) - - match query, variables with - | Some query, Some variables -> - printfn "Received query: %s" query - printfn "Received variables: %A" variables - let query = removeWhitespacesAndLineBreaks query - let root = { RequestId = System.Guid.NewGuid().ToString() } - let result = Schema.executor.AsyncExecute(query, root, variables) |> Async.RunSynchronously - printfn "Result metadata: %A" result.Metadata - return! okWithStr (json result) next ctx - | Some query, None -> - printfn "Received query: %s" query - let query = removeWhitespacesAndLineBreaks query - let result = Schema.executor.AsyncExecute(query) |> Async.RunSynchronously - printfn "Result metadata: %A" result.Metadata - return! okWithStr (json result) next ctx - | None, _ -> - let result = Schema.executor.AsyncExecute(Introspection.IntrospectionQuery) |> Async.RunSynchronously - printfn "Result metadata: %A" result.Metadata - return! okWithStr (json result) next ctx - } - - let webApp : HttpHandler = - setCorsHeaders - >=> graphQL - >=> setContentTypeAsJson diff --git a/samples/star-wars-api/JsonConverters.fs b/samples/star-wars-api/JsonConverters.fs deleted file mode 100644 index d5e8db6e7..000000000 --- a/samples/star-wars-api/JsonConverters.fs +++ /dev/null @@ -1,149 +0,0 @@ -namespace FSharp.Data.GraphQL.Samples.StarWarsApi - -open Newtonsoft.Json -open Newtonsoft.Json.Linq -open Microsoft.FSharp.Reflection -open FSharp.Data.GraphQL -open FSharp.Data.GraphQL.Types -open FSharp.Data.GraphQL.Types.Patterns - -[] -type OptionConverter() = - inherit JsonConverter() - - override __.CanConvert(t) = - t.IsGenericType && t.GetGenericTypeDefinition() = typedefof> - - override __.WriteJson(writer, value, serializer) = - let value = - if isNull value then null - else - let _,fields = Microsoft.FSharp.Reflection.FSharpValue.GetUnionFields(value, value.GetType()) - fields.[0] - serializer.Serialize(writer, value) - - override __.ReadJson(reader, t, _, serializer) = - let innerType = t.GetGenericArguments().[0] - let innerType = - if innerType.IsValueType then (typedefof>).MakeGenericType([|innerType|]) - else innerType - let value = serializer.Deserialize(reader, innerType) - let cases = FSharpType.GetUnionCases(t) - if isNull value then FSharpValue.MakeUnion(cases.[0], [||]) - else FSharpValue.MakeUnion(cases.[1], [|value|]) - -[] -type GraphQLQueryConverter<'a>(executor : Executor<'a>, replacements: Map, ?meta : Metadata) = - inherit JsonConverter() - - override __.CanConvert(t) = t = typeof - - override __.WriteJson(_, _, _) = failwith "Not supported" - - override __.ReadJson(reader, _, _, serializer) = - let jobj = JObject.Load reader - let query = jobj.Property("query").Value.ToString() - let plan = - match meta with - | Some meta -> executor.CreateExecutionPlan(query, meta = meta) - | None -> executor.CreateExecutionPlan(query) - let varDefs = plan.Variables - match varDefs with - | [] -> upcast { ExecutionPlan = plan; Variables = Map.empty } - | vs -> - // For multipart requests, we need to replace some variables - Map.iter(fun path rep -> jobj.SelectToken(path).Replace(JObject.FromObject(rep))) replacements - let vars = JObject.Parse(jobj.Property("variables").Value.ToString()) - let variables = - vs - |> List.fold (fun (acc: Map)(vdef: VarDef) -> - match vars.TryGetValue(vdef.Name) with - | true, jval -> - let v = - match jval.Type with - | JTokenType.Null -> null - | JTokenType.String -> jval.ToString() :> obj - | _ -> jval.ToObject(vdef.TypeDef.Type, serializer) - Map.add (vdef.Name) v acc - | false, _ -> - match vdef.DefaultValue, vdef.TypeDef with - | Some _, _ -> acc - | _, Nullable _ -> acc - | None, _ -> failwithf "Variable %s has no default value and is missing!" vdef.Name) Map.empty - upcast { ExecutionPlan = plan; Variables = variables } - -[] -type WebSocketClientMessageConverter<'a>(executor : Executor<'a>, replacements: Map, ?meta : Metadata) = - inherit JsonConverter() - - override __.CanWrite = false - - override __.CanConvert(t) = t = typeof - - override __.WriteJson(_, _, _) = failwith "Not supported" - - override __.ReadJson(reader, _, _, _) = - let jobj = JObject.Load reader - let typ = jobj.Property("type").Value.ToString() - match typ with - | "connection_init" -> upcast ConnectionInit - | "connection_terminate" -> upcast ConnectionTerminate - | "start" -> - let id = tryGetJsonProperty jobj "id" - let payload = tryGetJsonProperty jobj "payload" - match id, payload with - | Some id, Some payload -> - try - let settings = JsonSerializerSettings() - let queryConverter = - match meta with - | Some meta -> GraphQLQueryConverter(executor, replacements, meta) :> JsonConverter - | None -> GraphQLQueryConverter(executor, replacements) :> JsonConverter - let optionConverter = OptionConverter() :> JsonConverter - settings.Converters <- [| optionConverter; queryConverter |] - settings.ContractResolver <- Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver() - let req = JsonConvert.DeserializeObject(payload, settings) - upcast Start(id, req) - with e -> upcast ParseError(Some id, "Parse Failed with Exception: " + e.Message) - | None, _ -> upcast ParseError(None, "Malformed GQL_START message, expected id field but found none") - | _, None -> upcast ParseError(None, "Malformed GQL_START message, expected payload field but found none") - | "stop" -> - match tryGetJsonProperty jobj "id" with - | Some id -> upcast Stop(id) - | None -> upcast ParseError(None, "Malformed GQL_STOP message, expected id field but found none") - | typ -> upcast ParseError(None, "Message Type " + typ + " is not supported!") - -[] -type WebSocketServerMessageConverter() = - inherit JsonConverter() - - override __.CanRead = false - - override __.CanConvert(t) = t = typedefof || t.DeclaringType = typedefof - - override __.WriteJson(writer, value, _) = - let value = value :?> WebSocketServerMessage - let jobj = JObject() - match value with - | ConnectionAck -> - jobj.Add(JProperty("type", "connection_ack")) - | ConnectionError(err) -> - let errObj = JObject() - errObj.Add(JProperty("error", err)) - jobj.Add(JProperty("type", "connection_error")) - jobj.Add(JProperty("payload", errObj)) - | Error(id, err) -> - let errObj = JObject() - errObj.Add(JProperty("error", err)) - jobj.Add(JProperty("type", "error")) - jobj.Add(JProperty("payload", errObj)) - jobj.Add(JProperty("id", id)) - | Data(id, result) -> - jobj.Add(JProperty("type", "data")) - jobj.Add(JProperty("id", id)) - jobj.Add(JProperty("payload", JObject.FromObject(result))) - | Complete(id) -> - jobj.Add(JProperty("type", "complete")) - jobj.Add(JProperty("id", id)) - jobj.WriteTo(writer) - override __.ReadJson(_, _, _, _) = failwith "Not supported" diff --git a/samples/star-wars-api/Program.fs b/samples/star-wars-api/Program.fs index cd92f94c4..1591c0953 100644 --- a/samples/star-wars-api/Program.fs +++ b/samples/star-wars-api/Program.fs @@ -2,19 +2,49 @@ namespace FSharp.Data.GraphQL.Samples.StarWarsApi open Microsoft.AspNetCore open Microsoft.AspNetCore.Hosting +open System +open Microsoft.AspNetCore +open Microsoft.AspNetCore.Http.Features +open Microsoft.AspNetCore.Builder +open Microsoft.AspNetCore.Hosting +open Microsoft.Extensions.Logging +open Microsoft.Extensions.DependencyInjection +open FSharp.Data.GraphQL +open FSharp.Data.GraphQL.Server +open FSharp.Data.GraphQL.Server.AspNet module Program = - let exitCode = 0 + let configureApp (app : IApplicationBuilder) = + let schema = Schema(Schema.Query, Schema.Mutation, Schema.Subscription, Schema.schemaConfig) + let executor = Executor(schema) + let buildRoot ctx = async { return { RequestId = Guid.NewGuid().ToString() } } + app.UseGraphQL(executor, buildRoot, path="/") |> ignore - let [] BaseAddress = "localhost:8086" + let configureServices (services : IServiceCollection) = + services.AddDataProtection() |> ignore + services.Configure(fun (formOptions:FormOptions) -> + formOptions.MultipartBodyLengthLimit <- 2_147_483_648L + formOptions.ValueLengthLimit <- Int32.MaxValue + formOptions.BufferBodyLengthLimit <- 2147483648L + ) |> ignore - let buildWebHost args = - WebHost - .CreateDefaultBuilder(args) - .UseStartup() - .UseUrls(sprintf "http://%s" BaseAddress) + let configureLogging (loggerBuilder : ILoggingBuilder) = + loggerBuilder.AddFilter(fun lvl -> lvl.Equals LogLevel.Error) + .AddConsole() + .AddDebug() |> ignore [] - let main args = - buildWebHost(args).Build().Run() - exitCode + let main _ = + WebHost + .CreateDefaultBuilder() + .Configure(Action configureApp) + .ConfigureServices(configureServices) + .ConfigureLogging(configureLogging) + .UseKestrel(fun options -> + options.ListenAnyIP(8086) + options.Limits.MaxRequestBodySize <- Nullable 2147483648L + options.Limits.MaxRequestBufferSize <- Nullable 2147483648L + ) + .Build() + .Run() + 0 diff --git a/samples/star-wars-api/Schema.fs b/samples/star-wars-api/Schema.fs index 7ca7d2c71..a29af251c 100644 --- a/samples/star-wars-api/Schema.fs +++ b/samples/star-wars-api/Schema.fs @@ -3,6 +3,7 @@ open FSharp.Data.GraphQL open FSharp.Data.GraphQL.Types open FSharp.Data.GraphQL.Server.Middleware +open type FSharp.Data.GraphQL.Types.SchemaDefinitions.Define #nowarn "40" @@ -111,16 +112,16 @@ module Schema = characters |> List.tryFind (matchesId id) let EpisodeType = - Define.Enum( + Enum( name = "Episode", description = "One of the films in the Star Wars Trilogy.", options = [ - Define.EnumValue("NewHope", Episode.NewHope, "Released in 1977.") - Define.EnumValue("Empire", Episode.Empire, "Released in 1980.") - Define.EnumValue("Jedi", Episode.Jedi, "Released in 1983.") ]) + EnumValue("NewHope", Episode.NewHope, "Released in 1977.") + EnumValue("Empire", Episode.Empire, "Released in 1980.") + EnumValue("Jedi", Episode.Jedi, "Released in 1983.") ]) let rec CharacterType = - Define.Union( + Union( name = "Character", description = "A character in the Star Wars Trilogy.", options = [ HumanType; DroidType ], @@ -134,89 +135,89 @@ module Schema = | Droid _ -> upcast DroidType)) and HumanType : ObjectDef = - Define.Object( + Object( name = "Human", description = "A humanoid creature in the Star Wars universe.", isTypeOf = (fun o -> o :? Human), fieldsFn = fun () -> [ - Define.Field("id", String, "The id of the human.", fun _ (h : Human) -> h.Id) - Define.Field("name", Nullable String, "The name of the human.", fun _ (h : Human) -> h.Name) - Define.Field("friends", ListOf (Nullable CharacterType), "The friends of the human, or an empty list if they have none.", + Field("id", String, "The id of the human.", fun _ (h : Human) -> h.Id) + Field("name", Nullable String, "The name of the human.", fun _ (h : Human) -> h.Name) + Field("friends", SeqOf (Nullable CharacterType), "The friends of the human, or an empty list if they have none.", fun _ (h : Human) -> h.Friends |> List.map getCharacter |> List.toSeq).WithQueryWeight(0.5) - Define.Field("appearsIn", ListOf EpisodeType, "Which movies they appear in.", fun _ (h : Human) -> h.AppearsIn) - Define.Field("homePlanet", Nullable String, "The home planet of the human, or null if unknown.", fun _ h -> h.HomePlanet) + Field("appearsIn", ListOf EpisodeType, "Which movies they appear in.", fun _ (h : Human) -> h.AppearsIn) + Field("homePlanet", Nullable String, "The home planet of the human, or null if unknown.", fun _ h -> h.HomePlanet) ]) and DroidType = - Define.Object( + Object( name = "Droid", description = "A mechanical creature in the Star Wars universe.", isTypeOf = (fun o -> o :? Droid), fieldsFn = fun () -> [ - Define.Field("id", String, "The id of the droid.", fun _ (d : Droid) -> d.Id) - Define.Field("name", Nullable String, "The name of the Droid.", fun _ (d : Droid) -> d.Name) - Define.Field("friends", ListOf (Nullable CharacterType), "The friends of the Droid, or an empty list if they have none.", + Field("id", String, "The id of the droid.", fun _ (d : Droid) -> d.Id) + Field("name", Nullable String, "The name of the Droid.", fun _ (d : Droid) -> d.Name) + Field("friends", SeqOf (Nullable CharacterType), "The friends of the Droid, or an empty list if they have none.", fun _ (d : Droid) -> d.Friends |> List.map getCharacter |> List.toSeq).WithQueryWeight(0.5) - Define.Field("appearsIn", ListOf EpisodeType, "Which movies they appear in.", fun _ d -> d.AppearsIn) - Define.Field("primaryFunction", Nullable String, "The primary function of the droid.", fun _ d -> d.PrimaryFunction) + Field("appearsIn", ListOf EpisodeType, "Which movies they appear in.", fun _ d -> d.AppearsIn) + Field("primaryFunction", Nullable String, "The primary function of the droid.", fun _ d -> d.PrimaryFunction) ]) and PlanetType = - Define.Object( + Object( name = "Planet", description = "A planet in the Star Wars universe.", isTypeOf = (fun o -> o :? Planet), fieldsFn = fun () -> [ - Define.Field("id", String, "The id of the planet", fun _ p -> p.Id) - Define.Field("name", Nullable String, "The name of the planet.", fun _ p -> p.Name) - Define.Field("isMoon", Nullable Boolean, "Is that a moon?", fun _ p -> p.IsMoon) + Field("id", String, "The id of the planet", fun _ p -> p.Id) + Field("name", Nullable String, "The name of the planet.", fun _ p -> p.Name) + Field("isMoon", Nullable Boolean, "Is that a moon?", fun _ p -> p.IsMoon) ]) and RootType = - Define.Object( + Object( name = "Root", description = "The Root type to be passed to all our resolvers.", isTypeOf = (fun o -> o :? Root), fieldsFn = fun () -> [ - Define.Field("requestId", String, "The ID of the client.", fun _ (r : Root) -> r.RequestId) + Field("requestId", String, "The ID of the client.", fun _ (r : Root) -> r.RequestId) ]) let Query = - Define.Object( + Object( name = "Query", fields = [ - Define.Field("hero", Nullable HumanType, "Gets human hero", [ Define.Input("id", String) ], fun ctx _ -> getHuman (ctx.Arg("id"))) - Define.Field("droid", Nullable DroidType, "Gets droid", [ Define.Input("id", String) ], fun ctx _ -> getDroid (ctx.Arg("id"))) - Define.Field("planet", Nullable PlanetType, "Gets planet", [ Define.Input("id", String) ], fun ctx _ -> getPlanet (ctx.Arg("id"))) - Define.Field("characters", ListOf CharacterType, "Gets characters", fun _ _ -> characters) ]) + Field("hero", Nullable HumanType, "Gets human hero", [ Input("id", String) ], fun ctx _ -> getHuman (ctx.Arg("id"))) + Field("droid", Nullable DroidType, "Gets droid", [ Input("id", String) ], fun ctx _ -> getDroid (ctx.Arg("id"))) + Field("planet", Nullable PlanetType, "Gets planet", [ Input("id", String) ], fun ctx _ -> getPlanet (ctx.Arg("id"))) + Field("characters", ListOf CharacterType, "Gets characters", fun _ _ -> characters) ]) let Subscription = - Define.SubscriptionObject( + SubscriptionObject( name = "Subscription", fields = [ - Define.SubscriptionField( + SubscriptionField( "watchMoon", RootType, PlanetType, "Watches to see if a planet is a moon.", - [ Define.Input("id", String) ], + [ Input("id", String) ], (fun ctx _ p -> if ctx.Arg("id") = p.Id then Some p else None)) ]) let schemaConfig = SchemaConfig.Default let Mutation = - Define.Object( + Object( name = "Mutation", fields = [ - Define.Field( + Field( "setMoon", Nullable PlanetType, "Defines if a planet is actually a moon or not.", - [ Define.Input("id", String); Define.Input("isMoon", Boolean) ], + [ Input("id", String); Input("isMoon", Boolean) ], fun ctx _ -> getPlanet (ctx.Arg("id")) |> Option.map (fun x -> @@ -224,13 +225,8 @@ module Schema = schemaConfig.SubscriptionProvider.Publish "watchMoon" x schemaConfig.LiveFieldSubscriptionProvider.Publish "Planet" "isMoon" x x))]) - - let schema : ISchema = upcast Schema(Query, Mutation, Subscription, schemaConfig) - - let middlewares = - [ Define.QueryWeightMiddleware(2.0, true) - Define.ObjectListFilterMiddleware(true) - Define.ObjectListFilterMiddleware(true) - Define.LiveQueryMiddleware() ] - - let executor = Executor(schema, middlewares) \ No newline at end of file + let middlewares = + [ QueryWeightMiddleware(2.0, true) + ObjectListFilterMiddleware(true) + ObjectListFilterMiddleware(true) + LiveQueryMiddleware() ] \ No newline at end of file diff --git a/samples/star-wars-api/Startup.fs b/samples/star-wars-api/Startup.fs deleted file mode 100644 index f6091307a..000000000 --- a/samples/star-wars-api/Startup.fs +++ /dev/null @@ -1,32 +0,0 @@ -namespace FSharp.Data.GraphQL.Samples.StarWarsApi - -open Microsoft.AspNetCore.Builder -open Microsoft.Extensions.Configuration -open Microsoft.Extensions.DependencyInjection -open Giraffe -open Microsoft.Extensions.Logging -open System -open Microsoft.AspNetCore.Server.Kestrel.Core - -type Startup private () = - new (configuration: IConfiguration) as this = - Startup() then - this.Configuration <- configuration - - member __.ConfigureServices(services: IServiceCollection) = - services.AddGiraffe() - .Configure(Action(fun x -> x.AllowSynchronousIO <- true)) - .Configure(Action(fun x -> x.AllowSynchronousIO <- true)) - |> ignore - - member __.Configure(app: IApplicationBuilder) = - let errorHandler (ex : Exception) (log : ILogger) = - log.LogError(EventId(), ex, "An unhandled exception has occurred while executing the request.") - clearResponse >=> setStatusCode 500 - app - .UseGiraffeErrorHandler(errorHandler) - .UseWebSockets() - .UseMiddleware>(Schema.executor, fun () -> { RequestId = Guid.NewGuid().ToString() }) - .UseGiraffe HttpHandlers.webApp - - member val Configuration : IConfiguration = null with get, set diff --git a/samples/star-wars-api/WebSocketMessages.fs b/samples/star-wars-api/WebSocketMessages.fs deleted file mode 100644 index 258aaba58..000000000 --- a/samples/star-wars-api/WebSocketMessages.fs +++ /dev/null @@ -1,22 +0,0 @@ -namespace FSharp.Data.GraphQL.Samples.StarWarsApi - -open FSharp.Data.GraphQL.Execution -open FSharp.Data.GraphQL.Types - -type GraphQLQuery = - { ExecutionPlan : ExecutionPlan - Variables : Map } - -type WebSocketClientMessage = - | ConnectionInit - | ConnectionTerminate - | Start of id : string * payload : GraphQLQuery - | Stop of id : string - | ParseError of id : string option * err : string - -type WebSocketServerMessage = - | ConnectionAck - | ConnectionError of err : string - | Data of id : string * payload : Output - | Error of id : string option * err : string - | Complete of id : string diff --git a/samples/star-wars-api/WebSocketMiddleware.fs b/samples/star-wars-api/WebSocketMiddleware.fs deleted file mode 100644 index 046517c29..000000000 --- a/samples/star-wars-api/WebSocketMiddleware.fs +++ /dev/null @@ -1,157 +0,0 @@ -namespace FSharp.Data.GraphQL.Samples.StarWarsApi - -open System -open System.Threading -open System.Threading.Tasks -open System.Net.WebSockets -open Microsoft.AspNetCore.Http -open Newtonsoft.Json -open FSharp.Data.GraphQL -open FSharp.Data.GraphQL.Execution -open System.Collections.Generic -open System.Collections.Concurrent - -type GraphQLWebSocket(innerSocket : WebSocket) = - inherit WebSocket() - - let subscriptions = ConcurrentDictionary() :> IDictionary - let id = System.Guid.NewGuid() - - override __.CloseStatus = innerSocket.CloseStatus - - override __.CloseStatusDescription = innerSocket.CloseStatusDescription - - override __.State = innerSocket.State - - override __.SubProtocol = innerSocket.SubProtocol - - override __.CloseAsync(status, description, ct) = innerSocket.CloseAsync(status, description, ct) - - override __.CloseOutputAsync(status, description, ct) = innerSocket.CloseOutputAsync(status, description, ct) - - override this.Dispose() = - this.UnsubscribeAll() - innerSocket.Dispose() - - override __.ReceiveAsync(buffer : ArraySegment, ct) = innerSocket.ReceiveAsync(buffer, ct) - - override __.SendAsync(buffer : ArraySegment, msgType, endOfMsg, ct) = innerSocket.SendAsync(buffer, msgType, endOfMsg, ct) - - override __.Abort() = innerSocket.Abort() - - member __.Subscribe(id : string, unsubscriber : IDisposable) = - subscriptions.Add(id, unsubscriber) - - member __.Unsubscribe(id : string) = - match subscriptions.ContainsKey(id) with - | true -> - subscriptions.[id].Dispose() - subscriptions.Remove(id) |> ignore - | false -> () - - member __.UnsubscribeAll() = - subscriptions - |> Seq.iter (fun x -> x.Value.Dispose()) - subscriptions.Clear() - - member __.Id = id - -module SocketManager = - let private sockets = ConcurrentDictionary() :> IDictionary - - let private disposeSocket (socket : GraphQLWebSocket) = - sockets.Remove(socket.Id) |> ignore - socket.Dispose() - - let private sendMessage (socket : GraphQLWebSocket) (message : WebSocketServerMessage) = async { - let settings = - WebSocketServerMessageConverter() :> JsonConverter - |> Seq.singleton - |> jsonSerializerSettings - let json = JsonConvert.SerializeObject(message, settings) - let buffer = utf8Bytes json - let segment = new ArraySegment(buffer) - if socket.State = WebSocketState.Open then - do! socket.SendAsync(segment, WebSocketMessageType.Text, true, CancellationToken.None) |> Async.AwaitTask - else - disposeSocket socket - } - - let private receiveMessage (executor : Executor<'Root>) (replacements : Map) (socket : WebSocket) = async { - let buffer = Array.zeroCreate 4096 - let segment = ArraySegment(buffer) - do! socket.ReceiveAsync(segment, CancellationToken.None) - |> Async.AwaitTask - |> Async.Ignore - let message = utf8String buffer - if isNullOrWhiteSpace message - then - return None - else - let settings = - WebSocketClientMessageConverter(executor, replacements) :> JsonConverter - |> Seq.singleton - |> jsonSerializerSettings - return JsonConvert.DeserializeObject(message, settings) |> Some - } - - let private handleMessages (executor : Executor<'Root>) (root : unit -> 'Root) (socket : GraphQLWebSocket) = async { - let send id output = - Data (id, output) - |> sendMessage socket - |> Async.RunSynchronously - let sendDelayed id output = - Thread.Sleep(5000) - send id output - let handle id = - function - | Stream output -> - let unsubscriber = output |> Observable.subscribe (fun o -> send id o) - socket.Subscribe(id, unsubscriber) - | Deferred (data, _, output) -> - send id data - let unsubscriber = output |> Observable.subscribe (fun o -> sendDelayed id o) - socket.Subscribe(id, unsubscriber) - | Direct (data, _) -> - send id data - try - let mutable loop = true - while loop do - let! message = socket |> receiveMessage executor Map.empty - match message with - | Some ConnectionInit -> - do! sendMessage socket ConnectionAck - | Some (Start (id, payload)) -> - executor.AsyncExecute(payload.ExecutionPlan, root(), payload.Variables) - |> Async.RunSynchronously - |> handle id - do! Data (id, Dictionary()) |> sendMessage socket - | Some ConnectionTerminate -> - do! socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None) |> Async.AwaitTask - disposeSocket socket - loop <- false - | Some (ParseError (id, _)) -> - do! Error (id, "Invalid message type!") |> sendMessage socket - | Some (Stop id) -> - socket.Unsubscribe(id) - do! Complete id |> sendMessage socket - | None -> () - with - | _ -> disposeSocket socket - } - - let startSocket (socket : GraphQLWebSocket) (executor : Executor<'Root>) (root : unit -> 'Root) = - sockets.Add(socket.Id, socket) - handleMessages executor root socket |> Async.RunSynchronously - -type GraphQLWebSocketMiddleware<'Root>(next : RequestDelegate, executor : Executor<'Root>, root : unit -> 'Root) = - member __.Invoke(ctx : HttpContext) = - async { - match ctx.WebSockets.IsWebSocketRequest with - | true -> - let! socket = ctx.WebSockets.AcceptWebSocketAsync("graphql-ws") |> Async.AwaitTask - use socket = new GraphQLWebSocket(socket) - SocketManager.startSocket socket executor root - | false -> - next.Invoke(ctx) |> ignore - } |> Async.StartAsTask :> Task diff --git a/src/FSharp.Data.GraphQL.Client.DesignTime/DesignTimeCache.fs b/src/FSharp.Data.GraphQL.Client.DesignTime/DesignTimeCache.fs index 1d6ae5616..c0834f44d 100644 --- a/src/FSharp.Data.GraphQL.Client.DesignTime/DesignTimeCache.fs +++ b/src/FSharp.Data.GraphQL.Client.DesignTime/DesignTimeCache.fs @@ -10,18 +10,19 @@ open FSharp.Data.GraphQL.Client open ProviderImplementation.ProvidedTypes open FSharp.Data.GraphQL.Validation -type internal ProviderKey = +type internal ProviderSettings = { IntrospectionLocation : IntrospectionLocation CustomHttpHeadersLocation : StringLocation UploadInputTypeName : string option ResolutionFolder : string ClientQueryValidation: bool - ExplicitOptionalParameters: bool } + ExplicitOptionalParameters: bool + FragmentsFolder: string option } module internal ProviderDesignTimeCache = let private expiration = CacheExpirationPolicy.SlidingExpiration(TimeSpan.FromSeconds 30.0) - let private cache = MemoryCache(expiration) - let getOrAdd (key : ProviderKey) (defMaker : unit -> ProvidedTypeDefinition) = + let private cache = MemoryCache(expiration) + let getOrAdd (key : ProviderSettings) (defMaker : unit -> ProvidedTypeDefinition) = cache.GetOrAddResult key defMaker module internal QueryValidationDesignTimeCache = diff --git a/src/FSharp.Data.GraphQL.Client.DesignTime/FSharp.Data.GraphQL.Client.DesignTime.fsproj b/src/FSharp.Data.GraphQL.Client.DesignTime/FSharp.Data.GraphQL.Client.DesignTime.fsproj index 2cca4b284..3c5938e5e 100644 --- a/src/FSharp.Data.GraphQL.Client.DesignTime/FSharp.Data.GraphQL.Client.DesignTime.fsproj +++ b/src/FSharp.Data.GraphQL.Client.DesignTime/FSharp.Data.GraphQL.Client.DesignTime.fsproj @@ -22,20 +22,22 @@ paket-files/ProvidedTypes.fs - - + - - + - + + + + + diff --git a/src/FSharp.Data.GraphQL.Client.DesignTime/GraphQLProvider.DesignTime.fs b/src/FSharp.Data.GraphQL.Client.DesignTime/GraphQLProvider.DesignTime.fs index 8dc8c50a7..c31e4dd62 100644 --- a/src/FSharp.Data.GraphQL.Client.DesignTime/GraphQLProvider.DesignTime.fs +++ b/src/FSharp.Data.GraphQL.Client.DesignTime/GraphQLProvider.DesignTime.fs @@ -13,22 +13,13 @@ type GraphQLTypeProvider (config) as this = inherit TypeProviderForNamespaces(config, assemblyReplacementMap = [ "FSharp.Data.GraphQL.Client.DesignTime", "FSharp.Data.GraphQL.Client" - "FSharp.Data.GraphQL.Shared", "FSharp.Data.GraphQL.Shared" ], addDefaultProbingLocation = true) let ns = "FSharp.Data.GraphQL" let asm = Assembly.GetExecutingAssembly() - - do this.AddNamespace(ns, [Provider.makeProvidedType(asm, ns, config.ResolutionFolder)]) - - override this.ResolveAssembly args = - if args.Name.Contains("FSharp.Data.GraphQL") then - printfn "ResolveAssembly: %s" args.Name - config.ReferencedAssemblies - |> Array.filter(fun x -> x.Contains("FSharp.Data")) - |> Array.iter (fun x -> printfn "%s" x) - base.ResolveAssembly args + let provider = Provider.makeGraphQLProvider(asm, ns, config.ResolutionFolder) + do this.AddNamespace(ns, [provider]) [] do() \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Client.DesignTime/Helpers.fs b/src/FSharp.Data.GraphQL.Client.DesignTime/Helpers.fs new file mode 100644 index 000000000..4dbbf2c9a --- /dev/null +++ b/src/FSharp.Data.GraphQL.Client.DesignTime/Helpers.fs @@ -0,0 +1,72 @@ +namespace FSharp.Data.GraphQL + +open System +open System.Collections +open FSharp.Reflection +open FSharp.Quotations + +module internal QuotationHelpers = + let rec coerceValues fieldTypeLookup fields = + let arrayExpr (arrayType : Type) (v : obj) = + let typ = arrayType.GetElementType() + let instance = + match v with + | :? IEnumerable as x -> Seq.cast x |> Array.ofSeq + | _ -> failwith "Unexpected array value." + let exprs = coerceValues (fun _ -> typ) instance + Expr.NewArray(typ, exprs) + let tupleExpr (tupleType : Type) (v : obj) = + let typ = FSharpType.GetTupleElements tupleType |> Array.mapi (fun i t -> i, t) |> Map.ofArray + let fieldTypeLookup i = typ.[i] + let fields = FSharpValue.GetTupleFields v + let exprs = coerceValues fieldTypeLookup fields + Expr.NewTuple(exprs) + Array.mapi (fun i v -> + let expr = + if isNull v then simpleTypeExpr v + else + let tpy = v.GetType() + if tpy.IsArray then arrayExpr tpy v + elif FSharpType.IsTuple tpy then tupleExpr tpy v + elif FSharpType.IsUnion tpy then unionExpr v |> snd + elif FSharpType.IsRecord tpy then recordExpr v |> snd + else simpleTypeExpr v + Expr.Coerce(expr, fieldTypeLookup i) + ) fields |> List.ofArray + + and simpleTypeExpr instance = Expr.Value(instance) + + and unionExpr instance = + let caseInfo, fields = FSharpValue.GetUnionFields(instance, instance.GetType()) + let fieldInfo = caseInfo.GetFields() + let fieldTypeLookup indx = fieldInfo.[indx].PropertyType + caseInfo.DeclaringType, Expr.NewUnionCase(caseInfo, coerceValues fieldTypeLookup fields) + + and recordExpr instance = + let typ = instance.GetType() + let fields = FSharpValue.GetRecordFields(instance) + let fieldInfo = FSharpType.GetRecordFields(typ) + let fieldTypeLookup indx = fieldInfo.[indx].PropertyType + typ, Expr.NewRecord(instance.GetType(), coerceValues fieldTypeLookup fields) + + and arrayExpr (instance : 'a array) = + let typ = typeof<'a> + let arrayType = instance.GetType() + let exprs = coerceValues (fun _ -> typ) (instance |> Array.map box) + arrayType, Expr.NewArray(typ, exprs) + + let createLetExpr varType instance body args = + let var = Var("instance", varType) + Expr.Let(var, instance, body args (Expr.Var(var))) + + let quoteUnion instance = + let func instance = unionExpr instance ||> createLetExpr + Tracer.runAndMeasureExecutionTime "Quoted union type" (fun _ -> func instance) + + let quoteRecord instance = + let func instance = recordExpr instance ||> createLetExpr + Tracer.runAndMeasureExecutionTime "Quoted record type" (fun _ -> func instance) + + let quoteArray instance = + let func instance = arrayExpr instance ||> createLetExpr + Tracer.runAndMeasureExecutionTime "Quoted array type" (fun _ -> func instance) diff --git a/src/FSharp.Data.GraphQL.Client.DesignTime/OperationGenerator.fs b/src/FSharp.Data.GraphQL.Client.DesignTime/OperationGenerator.fs new file mode 100644 index 000000000..346085c53 --- /dev/null +++ b/src/FSharp.Data.GraphQL.Client.DesignTime/OperationGenerator.fs @@ -0,0 +1,451 @@ +namespace FSharp.Data.GraphQL + +open System +open System.IO +open System.Text +open FSharp.Core +open System.Reflection +open FSharp.Data.GraphQL +open FSharp.Data.GraphQL.Client +open FSharp.Data.GraphQL.Client.DesignTimeSerialization +open FSharp.Data.GraphQL.Client.ReflectionPatterns +open FSharp.Data.GraphQL.Ast +open FSharp.Data.GraphQL.Validation +open System.Collections.Generic +open FSharp.Data.GraphQL.Types.Introspection +open FSharp.Data.GraphQL.Ast.Extensions +open FSharp.Data.GraphQL.ProvidedSchema +open ProviderImplementation.ProvidedTypes +open FSharp.Quotations + +#nowarn "10001" + +type internal ProvidedOperationMetadata = + { OperationType : Type + UploadInputTypeName : string option + TypeWrapper : ProvidedTypeDefinition } + +[] +module private Operations = + + let makeProvidedOperationResultType (operationType : Type) = + let tdef = ProvidedTypeDefinition("OperationResult", Some typeof, nonNullable = true) + tdef.AddMemberDelayed(fun _ -> + let getterCode (args : Expr list) = + <@@ let this = %%args.[0] : OperationResultBase + this.RawData @@> + let prop = ProvidedProperty("Data", operationType, getterCode) + prop.AddXmlDoc("Contains the data returned by the operation on the server.") + prop) + tdef + + let convertToClrTypes (schemaGenerator: SchemaGenerator) (variables: VariableDefinition list) = + let schemaTypes = schemaGenerator.SchemaTypes + let isCustomScalar (typeName: string) = + match schemaTypes.TryFindType typeName with + | Some introspectionType -> introspectionType.Kind = TypeKind.SCALAR + | None -> false + let getSchemaType (typeName: string) = + match schemaTypes.UploadInputType with + | Some uploadInputTypeName when typeName = uploadInputTypeName.Name -> + typeof + | _ -> + match TypeMapping.scalar.TryFind(typeName) with + | Some t -> t + | None when isCustomScalar typeName -> typeof + | None -> + match schemaGenerator.ProvidedTypes.TryGetValue(typeName) with + | true, t -> t :> Type + | false, _ -> failwithf "Unable to find variable type \"%s\" in the schema definition." typeName + let rec mapVariable (variableType : InputType) (isNullable: bool) = + match variableType with + | NamedType typeName when isNullable -> getSchemaType typeName |> TypeMapping.makeOption + | NamedType typeName -> getSchemaType typeName + | ListType itype when isNullable -> mapVariable itype true |> TypeMapping.makeArray |> TypeMapping.makeOption + | ListType itype -> mapVariable itype true |> TypeMapping.makeArray + | NonNullType itype -> mapVariable itype false + List.map (fun vdef -> vdef.VariableName, mapVariable vdef.Type true) variables + + + // We need to use the combination strategy to generate overloads for variables in the Run/AsyncRun methods. + // The strategy follows the same principle with ProvidedRecord constructor overloads, + // except that we also must create one additional overload for the runtime context, for each already existent overload, + // if no default context is provided. + let createRunMethodOverloads contextInfo explicitOptionalParameters clrVariables = + let overloadsWithoutContext = + let optionalVariables, requiredVariables = + clrVariables |> List.partition (fun (_, t) -> isOption t) + if explicitOptionalParameters then + [requiredVariables @ optionalVariables] + else + optionalVariables + |> List.combinations + |> List.map (fun (optionalVariables, _) -> + let optionalVariables = optionalVariables |> List.map (fun (name, t) -> name, TypeMapping.unwrapOption t) + requiredVariables @ optionalVariables) + let overloadsWithContext = + overloadsWithoutContext + |> List.map (fun var -> ("runtimeContext", typeof) :: var) + match contextInfo with + | Some _ -> overloadsWithoutContext @ overloadsWithContext + | None -> overloadsWithContext + + let rec existsUploadType (foundTypes : ProvidedTypeDefinition list) (t : Type) = + match t with + | :? ProvidedTypeDefinition as tdef when not (List.contains tdef foundTypes) -> + tdef.DeclaredProperties |> Seq.exists ((fun p -> p.PropertyType) >> existsUploadType (tdef :: foundTypes)) + | Option t -> + existsUploadType foundTypes t + | Array t -> + existsUploadType foundTypes t + | _ -> + t = typeof + + let makeProvidedOperationType + (actualQuery: string, operationDefinition: OperationDefinition, operationTypeName: string, + schemaGenerator: SchemaGenerator, operationType: Type, selections: list, + contextInfo: GraphQLRuntimeContextInfo option, className: string, explicitOptionalParameters: bool) = + + let tdef = ProvidedTypeDefinition(className, Some typeof) + tdef.AddXmlDoc("Represents a GraphQL operation on the server.") + tdef.AddMembersDelayed(fun _ -> + let operationResultDef = makeProvidedOperationResultType operationType + let clrVariables = convertToClrTypes schemaGenerator operationDefinition.VariableDefinitions + let defaultContextExpr = + match contextInfo with + | Some info -> + let serverUrl = info.ServerUrl + let headerNames = info.HttpHeaders |> Seq.map fst |> Array.ofSeq + let headerValues = info.HttpHeaders |> Seq.map snd |> Array.ofSeq + <@@ { ServerUrl = serverUrl; HttpHeaders = Array.zip headerNames headerValues; Connection = new GraphQLClientConnection() } @@> + | None -> + <@@ Unchecked.defaultof @@> + + let methodOverloadDefinitions = createRunMethodOverloads contextInfo explicitOptionalParameters clrVariables + // Multipart requests should only be used when the user specifies a upload type name AND the type + // is present in the query as an input value. If not, we fallback to classic requests. + let shouldUseMultipartRequest = clrVariables |> Seq.exists (snd >> existsUploadType []) + let jsonParser = getOperationJsonReader schemaGenerator.SchemaTypes selections operationDefinition + let runMethodOverloads : MemberInfo list = + let operationName = Option.toObj operationDefinition.Name + [ for overloadParameters in methodOverloadDefinitions do + let variableNames = + [ for (name, _) in overloadParameters do + if name <> "runtimeContext" then + name ] + let invoker (isAsync: bool) (args : Expr list) = + // First arg is the operation instance, second should be the context, if the overload asks for one. + // We determine it by seeing if the variable names have one less item than the arguments without the instance. + let argsWithoutInstance = args.Tail + let variableArgs, isDefaultContext, context = + if argsWithoutInstance.Length - variableNames.Length = 1 + then argsWithoutInstance.Tail, false, argsWithoutInstance.Head + else argsWithoutInstance, true, defaultContextExpr + let variables = + let elements = + variableArgs + |> List.skip (variableArgs.Length - clrVariables.Length) + |> List.map2(fun name value -> + let expr = Expr.Coerce(value, typeof) + <@@ name, %%expr @@>) variableNames + Expr.NewArray(typeof, elements) + let runExpr = + <@ let context = %%context : GraphQLProviderRuntimeContext + let request = + { ServerUrl = context.ServerUrl; HttpHeaders = context.HttpHeaders + OperationName = Option.ofObj operationName; Query = actualQuery + Variables = %%variables } + async { + let! response = + if shouldUseMultipartRequest + then Tracer.asyncRunAndMeasureExecutionTime "Ran a multipart GraphQL query request asynchronously" (fun _ -> GraphQLClient.sendMultipartRequestAsync context.Connection request) + else Tracer.asyncRunAndMeasureExecutionTime "Ran a GraphQL query request asynchronously" (fun _ -> GraphQLClient.sendRequestAsync context.Connection request) + // If the user does not provide a context, we should dispose the default one after running the query + if isDefaultContext then (context :> IDisposable).Dispose() + return OperationResultBase(response, %%jsonParser, operationTypeName) + } @> + if isAsync then + runExpr :> Expr + else + <@@ %runExpr |> Async.RunSynchronously @@> + + let methodParameters = overloadParameters |> List.map (fun (name, t) -> ProvidedParameter(name, t, ?optionalValue = if isOption t then Some null else None)) + + let asyncMethodDef = ProvidedMethod("AsyncRun", methodParameters, TypeMapping.makeAsync operationResultDef, invoker true) + asyncMethodDef.AddXmlDoc("Executes the operation asynchronously on the server and fetch its results.") + asyncMethodDef + + let methodDef = ProvidedMethod("Run", methodParameters, operationResultDef, invoker false) + methodDef.AddXmlDoc("Executes the operation synchronously on the server and fetch its results.") + methodDef ] + + let parseResultDef = + let invoker (args : Expr list) = + <@@ let bytes = Encoding.UTF8.GetBytes(%%args.[1] : string) + let stream = new MemoryStream(bytes) + OperationResultBase(stream , %%jsonParser, operationTypeName) @@> + let parameters = [ProvidedParameter("responseJson", typeof)] + let methodDef = ProvidedMethod("ParseResult", parameters, operationResultDef, invoker) + methodDef.AddXmlDoc("Parses a JSON response that matches the response pattern of the current operation into a OperationResult type.") + methodDef + + [ yield! runMethodOverloads + operationResultDef + parseResultDef ]) + tdef + + + let makeProvidedType (getWrapper: SchemaTypes -> Path -> ProvidedTypeDefinition) (schemaGenerator: SchemaGenerator) (rootAstFields: AstFieldInfo list) (rootTypeRef: IntrospectionTypeRef) (explicitOptionalParameters: bool): Type = + let providedTypes = Dictionary<_,_>() + let includeType (path: string list) (t: ProvidedTypeDefinition) = + let wrapper = getWrapper schemaGenerator.SchemaTypes path + wrapper.AddMember(t) + let uploadInputTypeName = schemaGenerator.SchemaTypes.UploadInputType |> Option.map(fun n -> n.Name) + let rec makeType (path: string list) (astFields: AstFieldInfo list) (tref: IntrospectionTypeRef) (isNullable: bool) = + match tref with + | NamedTypeRef(TypeKind.SCALAR, name) -> + let scalarType = TypeMapping.mapScalarType uploadInputTypeName name + if isNullable + then TypeMapping.makeOption scalarType + else scalarType + | NamedTypeRef(TypeKind.ENUM, name) -> + match schemaGenerator.EnumProvidedTypes.TryGetValue(name) with + | true, enumType -> + if isNullable + then TypeMapping.makeOption enumType + else enumType :> Type + | false, _ -> failwithf "Could not find a enum type based on a type reference. The reference is an \"%s\" enum, but that enum was not found in the introspection schema." tref.Name.Value + | NamedTypeRef((TypeKind.OBJECT | TypeKind.INTERFACE | TypeKind.UNION), name) -> + match providedTypes.TryGetValue ((path, name)) with + | true, providedType -> + if isNullable + then TypeMapping.makeOption providedType + else providedType :> Type + | false, _ -> + let getIntrospectionFields typeName = + match schemaGenerator.SchemaTypes.TryFindType(typeName) with + | Some def -> Option.defaultValue [||] def.Fields + | None -> failwithf "Could not find a schema type based on a type reference. The reference is to a \"%s\" type, but that type was not found in the schema types." typeName + let getPropertyMetadata typeName (info : AstFieldInfo) : RecordPropertyMetadata = + let ifield = + match getIntrospectionFields typeName |> Array.tryFind(fun f -> f.Name = info.Name) with + | Some ifield -> ifield + | None -> failwithf "Could not find field \"%s\" of type \"%s\". The schema type does not have a field with the specified name." info.Name tref.Name.Value + let path = info.AliasOrName :: path + let astFields = info.Fields + let ftype = makeType path astFields ifield.Type true + { Name = info.Name; Alias = info.Alias; Description = ifield.Description; DeprecationReason = ifield.DeprecationReason; Type = ftype } + let fragmentProperties = + astFields + |> List.choose (function FragmentField f when f.TypeCondition <> tref.Name.Value -> Some f | _ -> None) + |> List.groupBy (fun field -> field.TypeCondition) + |> List.map (fun (typeCondition, fields) -> + let conditionFields = fields |> List.distinctBy (fun x -> x.AliasOrName) |> List.map FragmentField + typeCondition, List.map (getPropertyMetadata typeCondition) conditionFields) + let baseProperties = + astFields + |> List.choose (fun x -> + match x with + | TypeField _ -> Some x + | FragmentField f when f.TypeCondition = tref.Name.Value -> Some x + | _ -> None) + |> List.distinctBy (fun x -> x.AliasOrName) + |> List.map (getPropertyMetadata tref.Name.Value) + let baseType = + let metadata : ProvidedTypeMetadata = { Name = tref.Name.Value; Description = tref.Description } + let tdef = preBuildProvidedRecordType(metadata, None) + providedTypes.Add((path, tref.Name.Value), tdef) + includeType path tdef + makeProvidedRecordType(tdef, baseProperties, explicitOptionalParameters) + + for (typeName, properties) in fragmentProperties do + match schemaGenerator.SchemaTypes.TryFindType(typeName) with + | Some tp -> + let metadata = { Name = tp.Name; Description = tp.Description } + let tdef = preBuildProvidedRecordType(metadata, Some (upcast baseType)) + providedTypes.Add((path, typeName), tdef) + includeType path tdef + makeProvidedRecordType(tdef, properties, explicitOptionalParameters) |> ignore + | None -> + failwithf "Could not find schema type based on the query. Type \"%s\" does not exist on the schema definition." typeName + + if isNullable + then TypeMapping.makeOption baseType + else baseType :> Type + | ContainerTypeRef(TypeKind.LIST, innerTypeRef) -> + let elementType = makeType path astFields innerTypeRef true + let arrayType = TypeMapping.makeArray elementType + if isNullable + then TypeMapping.makeOption arrayType + else arrayType + | ContainerTypeRef(TypeKind.NON_NULL, innerTypeRef) -> + makeType path astFields innerTypeRef false + | typeRef -> + failwithf "Unsupported Type ref encountered while generating Operation '%A'" typeRef + makeType [] rootAstFields rootTypeRef true + + let rec getKind (tref : IntrospectionTypeRef) = + match tref with + | ContainerTypeRef(_, inner) -> getKind inner + | _ -> tref.Kind + + let rec getTypeName (tref: IntrospectionTypeRef) = + match tref with + | ContainerTypeRef(_, inner) -> getTypeName inner + | NamedTypeRef(_, name) -> name + + let rec getIntrospectionType (schemaTypes: SchemaTypes) (tref: IntrospectionTypeRef) = + match tref with + | ContainerTypeRef(_, inner) -> + getIntrospectionType schemaTypes inner + | NamedTypeRef(_, name) -> + match schemaTypes.TryFindType name with + | Some t -> t + | None -> failwithf "Type \"%s\" was not found in the introspection schema." name + +type GeneratedOperation = + { Query: string + ActualQuery: string + OperationType: ProvidedTypeDefinition } + +type internal OperationGenerator (providerSettings: ProviderSettings, schemaGenerator: SchemaGenerator, httpHeaders: seq, operationWrapper: ProvidedTypeDefinition) = + let wrappersByPath = Dictionary() + let rootWrapper = ProvidedTypeDefinition("Types", Some typeof, isSealed = true) + let getWrapper (schemaTypes: SchemaTypes) (path: string list) = + let rec resolveWrapperName actual = + if schemaTypes.ContainsType actual + then resolveWrapperName (actual + "Fields") + else actual + let rec getWrapperForPath (path : string list) = + match wrappersByPath.TryGetValue(path) with + | true, wrapper -> + wrapper + | false, _ -> + match path with + | hd::tail -> + let typeName = hd.ToTitleCase() + "Fields" + let wrapper = ProvidedTypeDefinition(resolveWrapperName typeName, Some typeof, isSealed = true) + let upperWrapper: ProvidedTypeDefinition = + if wrappersByPath.ContainsKey(tail) + then wrappersByPath.[tail] + else getWrapperForPath tail + upperWrapper.AddMember(wrapper) + wrappersByPath.Add(path, wrapper) + wrapper + | [] -> + rootWrapper + getWrapperForPath path + + +#if IS_DESIGNTIME + let throwExceptionIfValidationFailed (validationResult : ValidationResult) = + let rec formatValidationExceptionMessage(errors : AstError list) = + match errors with + | [] -> "Query validation resulted in invalid query, but no validation messages were produced." + | errors -> + errors + |> List.map (fun err -> + match err.Path with + | Some path -> sprintf "%s Path: %A" err.Message path + | None -> err.Message) + |> List.reduce (fun x y -> x + Environment.NewLine + y) + match validationResult with + | ValidationError msgs -> failwith (formatValidationExceptionMessage msgs) + | Success -> () +#endif + + let tryPickOperation (selectedOperationName: string option) (items: Definition list) = + match selectedOperationName with + | Some _ -> + List.tryPick(fun definition -> + match definition with + | OperationDefinition op when op.Name = selectedOperationName -> + Some op + | _ -> + None + ) items + | None -> + List.tryPick(fun definition -> + match definition with + | OperationDefinition op -> + Some op + | _ -> + None + ) items + + let getRootType (schema: IntrospectionSchema) (operation: OperationDefinition) = + let tref = + match operation.OperationType with + | Query -> schema.QueryType + | Mutation -> + match schema.MutationType with + | Some tref -> tref + | None -> failwith "The operation is a mutation operation, but the schema does not have a mutation type." + | Subscription -> + match schema.SubscriptionType with + | Some tref -> tref + | None -> failwithf "The operation is a subscription operation, but the schema does not have a subscription type." + let tinst = + match tref.Name with + | Some name -> schema.Types |> Array.tryFind (fun t -> t.Name = name) + | None -> None + match tinst with + | Some t -> { tref with Kind = t.Kind } + | None -> failwith "The operation was found in the schema, but it does not have a name." + + let generateOperationDefinition (schema: IntrospectionSchema) (query: string) (queryAst: Document) (operation: OperationDefinition) (explicitOperationTypeName: string option) = +#if IS_DESIGNTIME + if providerSettings.ClientQueryValidation then + let key = { DocumentId = queryAst.GetHashCode(); SchemaId = schema.GetHashCode() } + fun () -> Ast.validateDocument schema queryAst + |> QueryValidationDesignTimeCache.getOrAdd key + |> throwExceptionIfValidationFailed +#endif + let infoMap = queryAst.GetInfoMap() + let astFields = + match infoMap.TryFind(operation.Name) with + | Some fields -> fields + | None -> failwith "Error parsing query. Could not find field information for requested operation." + let rootType = getRootType schema operation + let actualQuery = queryAst.ToQueryString(QueryStringPrintingOptions.IncludeTypeNames).Replace("\r\n", "\n") + let className = + match explicitOperationTypeName, operation.Name with + | Some name, _ + | None, Some name -> name.ToTitleCase() + | None, None -> "Operation" + actualQuery.MD5Hash() + let metadata = + let operationType = makeProvidedType getWrapper schemaGenerator astFields rootType providerSettings.ExplicitOptionalParameters + let uploadInputTypeName = schemaGenerator.SchemaTypes.UploadInputType |> Option.map (fun t -> t.Name) + let rootWrapper = getWrapper schemaGenerator.SchemaTypes [] + { OperationType = operationType + UploadInputTypeName = uploadInputTypeName + TypeWrapper = rootWrapper } + let operationTypeName : string = + match rootType.Name with + | Some name -> name + | None -> failwith "Error parsing query. Operation type does not have a name." + + let contextInfo : GraphQLRuntimeContextInfo option = + match providerSettings.IntrospectionLocation with + | Uri serverUrl -> Some { ServerUrl = serverUrl; HttpHeaders = httpHeaders } + | _ -> None + let operationDef = makeProvidedOperationType(actualQuery, operation, operationTypeName, schemaGenerator, metadata.OperationType, astFields, contextInfo, className, providerSettings.ExplicitOptionalParameters) + operationDef.AddMember(metadata.TypeWrapper) + { Query = query + ActualQuery = actualQuery + OperationType = operationDef } + + member _.Generate (queryOrPath: string, resolutionFolder: string, ?operationName: string, ?typeName: string): GeneratedOperation = + let queryLocation = StringLocation.Create(queryOrPath, resolutionFolder) + let query = + match queryLocation with + | String query -> query + | File path -> File.ReadAllText(path) + let queryAst = Parser.parse query + let schema = schemaGenerator.SchemaTypes.Introspection + let operationDefinition = tryPickOperation operationName queryAst.Definitions + match operationDefinition with + | Some operation -> + generateOperationDefinition schema query queryAst operation typeName + | None -> + failwith "Expected an operation definition" \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Client.DesignTime/ProvidedSchema.fs b/src/FSharp.Data.GraphQL.Client.DesignTime/ProvidedSchema.fs new file mode 100644 index 000000000..1c2530197 --- /dev/null +++ b/src/FSharp.Data.GraphQL.Client.DesignTime/ProvidedSchema.fs @@ -0,0 +1,175 @@ +module internal FSharp.Data.GraphQL.ProvidedSchema + +open System +open System.Collections.Generic +open FSharp.Core +open System.Reflection +open FSharp.Data.GraphQL +open FSharp.Data.GraphQL.Client +open FSharp.Data.GraphQL.Client.ReflectionPatterns +open ProviderImplementation.ProvidedTypes +open FSharp.Quotations + +type internal ProvidedTypeMetadata = + { Name : string + Description : string option } + +type internal RecordPropertyMetadata = + { Name : string + Alias : string option + Description : string option + DeprecationReason : string option + Type : Type } + member x.AliasOrName = + match x.Alias with + | Some x -> x + | None -> x.Name + +let enumCtor = typeof.GetConstructors().[0] +let makeProvidedEnumType (name, items : string seq) = + let tdef = ProvidedTypeDefinition(name, Some typeof, nonNullable = true, isSealed = true) + tdef.AddMembersDelayed(fun _ -> + items + |> Seq.map (fun item -> + let getterCode (_ : Expr list) = + Expr.NewObject(enumCtor, [ <@@ name @@>; <@@ item @@> ]) + ProvidedProperty(item, tdef, getterCode, isStatic = true)) + |> Seq.cast + |> List.ofSeq) + tdef + +let makeProvidedInterfaceType(metadata : ProvidedTypeMetadata) = + let tdef = ProvidedTypeDefinition("I" + metadata.Name.ToTitleCase(), None, nonNullable = true, isInterface = true) + metadata.Description |> Option.iter tdef.AddXmlDoc + tdef + +type internal ProvidedRecordTypeDefinition(className, baseType) = + inherit ProvidedTypeDefinition(className, baseType, nonNullable = true) + + let mutable properties : RecordPropertyMetadata list = [] + + member __.GetRecordProperties() = properties + + member __.SetRecordProperties(props) = properties <- props + + +let recordCtor = typeof.GetConstructors().[0] +let makeProvidedRecordType (tdef : ProvidedRecordTypeDefinition, properties : RecordPropertyMetadata list, explicitOptionalParameters: bool) = + let name = tdef.Name + tdef.AddMembersDelayed(fun _ -> + properties |> List.map (fun metadata -> + let pname = metadata.AliasOrName + let getterCode (args : Expr list) = + <@@ let this = %%args.[0] : RecordBase + match this.TryGetProperty(pname) with + | Some value -> value + | None -> null @@> + + let pdef = ProvidedProperty(pname.ToTitleCase(), metadata.Type, getterCode) + metadata.Description |> Option.iter pdef.AddXmlDoc + metadata.DeprecationReason |> Option.iter pdef.AddObsoleteAttribute + pdef)) + let addConstructorDelayed (propertiesGetter : unit -> (string * string option * Type) list) = + tdef.AddMembersDelayed(fun _ -> + // We need to build a constructor that takes all optional properties wrapped in another option. + // We need to do this because optional parameters have issues with non-nullable types + // in the type provider SDK. They require a default value, and if the type is not nullable + // it provides the type default value for it. So basically, what's needed is three-valued behavior + // so we can differentiate between no value and null/None. + // + // I.e. to not send a value the optional parameter can either be set implicitly (by not providing an + // argument) or explicitly (`?parameterName = Some None` or `parameterName = None`). + // To set a value it has to be wrapped in an option: `parameterName = Some argumentValue` + // (or `?parameterName = Some (Some argumentValue)`). + // + // To keep backwards compatibility this constructor is only created if a flag is turned on. Otherwise + // we keep the previous behavior: We build a constructor overload for each optional property. + // Since RecordBase needs to know each property information in its own constructor, + // we also need to know each property that was not filled in the currently used overload. So we make + // combinations of all possible overloads, and for each one we map the user's provided values and + // fill the others with a null value. This way we can construct the RecordBase type providing all + // needed properties. + let properties = propertiesGetter() + let optionalProperties, requiredProperties = + properties + |> List.map (fun (name, alias, t) -> Option.defaultValue name alias, t) + |> List.partition (fun (_, t) -> isOption t) + if explicitOptionalParameters then + let constructorProperties = requiredProperties @ optionalProperties + let propertyNames = constructorProperties |> List.map fst + let invoker (args : Expr list) = + let properties = + let baseConstructorArgs = + (propertyNames, args) + ||> List.map2 (fun name value -> + let expr = Expr.Coerce(value, typeof) + <@@ (name, %%expr) @@>) + let arg = Expr.NewArray(typeof, baseConstructorArgs) + <@ dict (%%arg : array) @> + Expr.NewObject(recordCtor, [Expr.Value(name); properties]) + let constructorParams = + constructorProperties + |> List.map (fun (name, t) -> ProvidedParameter(name, t, ?optionalValue = if isOption t then Some null else None)) + [ProvidedConstructor(constructorParams, invoker)] + else + List.combinations optionalProperties + |> List.map (fun (optionalProperties, nullValuedProperties) -> + let constructorProperties = requiredProperties @ optionalProperties + let propertyNames = (constructorProperties @ nullValuedProperties) |> List.map fst + let constructorPropertyTypes = constructorProperties |> List.map snd + let nullValuedPropertyTypes = nullValuedProperties |> List.map snd + let invoker (args : Expr list) = + let properties = + let baseConstructorArgs = + let coercedArgs = + (constructorPropertyTypes, args) + ||> List.map2 (fun t arg -> + let arg = Expr.Coerce(arg, typeof) + if isOption t then <@@ makeSome %%arg @@> else <@@ %%arg @@>) + let nullValuedArgs = nullValuedPropertyTypes |> List.map (fun _ -> <@@ null @@>) + (propertyNames, (coercedArgs @ nullValuedArgs)) + ||> List.map2 (fun name value -> <@@ (name, %%value) @@>) + let arg = Expr.NewArray(typeof, baseConstructorArgs) + <@ dict (%%arg : array) @> + Expr.NewObject(recordCtor, [Expr.Value(name); properties]) + let constructorParams = + constructorProperties + |> List.map (fun (name, t) -> + match t with + | Option t -> ProvidedParameter(name, t) + | _ -> ProvidedParameter(name, t)) + ProvidedConstructor(constructorParams, invoker))) + match tdef.BaseType with + | :? ProvidedRecordTypeDefinition as bdef -> + bdef.AddMembersDelayed(fun _ -> + let asType = + let invoker (args : Expr list) = + <@@ let this = %%args.[0] : RecordBase + if this.GetName() = name then this + else failwithf "Expected type to be \"%s\", but it is \"%s\". Make sure to check the type by calling \"Is%s\" method before calling \"As%s\" method." name (this.GetName()) name name @@> + ProvidedMethod("As" + name, [], tdef, invoker) + let tryAsType = + let invoker (args : Expr list) = + <@@ let this = %%args.[0] : RecordBase + if this.GetName() = name then Some this + else None @@> + ProvidedMethod("TryAs" + name, [], typedefof<_ option>.MakeGenericType(tdef), invoker) + let isType = + let invoker (args : Expr list) = + <@@ let this = %%args.[0] : RecordBase + this.GetName() = name @@> + ProvidedMethod("Is" + name, [], typeof, invoker) + let members : MemberInfo list = [asType; tryAsType; isType] + members) + let propertiesGetter() = bdef.GetRecordProperties() @ properties |> List.map (fun p -> p.Name, p.Alias, p.Type) + addConstructorDelayed propertiesGetter + | _ -> + let propertiesGetter() = properties |> List.map (fun p -> p.Name, p.Alias, p.Type) + addConstructorDelayed propertiesGetter + tdef.SetRecordProperties(properties) + tdef + +let preBuildProvidedRecordType(metadata : ProvidedTypeMetadata, baseType : Type option) = + let baseType = Option.defaultValue typeof baseType + let name = metadata.Name.ToTitleCase() + ProvidedRecordTypeDefinition(name, Some baseType) \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Client.DesignTime/ProvidedTypesHelper.fs b/src/FSharp.Data.GraphQL.Client.DesignTime/ProvidedTypesHelper.fs deleted file mode 100644 index 3ee46e852..000000000 --- a/src/FSharp.Data.GraphQL.Client.DesignTime/ProvidedTypesHelper.fs +++ /dev/null @@ -1,940 +0,0 @@ -/// The MIT License (MIT) -/// Copyright (c) 2016 Bazinga Technologies Inc - -namespace FSharp.Data.GraphQL - -open System -open FSharp.Core -open System.Reflection -open FSharp.Data.GraphQL -open FSharp.Data.GraphQL.Client -open FSharp.Data.GraphQL.Client.ReflectionPatterns -open FSharp.Data.GraphQL.Ast -open FSharp.Data.GraphQL.Validation -open System.Collections.Generic -open FSharp.Data.GraphQL.Types.Introspection -open FSharp.Data.GraphQL.Ast.Extensions -open ProviderImplementation.ProvidedTypes -open Microsoft.FSharp.Quotations -open Microsoft.FSharp.Reflection -open System.Collections - -module internal QuotationHelpers = - let rec coerceValues fieldTypeLookup fields = - let arrayExpr (arrayType : Type) (v : obj) = - let typ = arrayType.GetElementType() - let instance = - match v with - | :? IEnumerable as x -> Seq.cast x |> Array.ofSeq - | _ -> failwith "Unexpected array value." - let exprs = coerceValues (fun _ -> typ) instance - Expr.NewArray(typ, exprs) - let tupleExpr (tupleType : Type) (v : obj) = - let typ = FSharpType.GetTupleElements tupleType |> Array.mapi (fun i t -> i, t) |> Map.ofArray - let fieldTypeLookup i = typ.[i] - let fields = FSharpValue.GetTupleFields v - let exprs = coerceValues fieldTypeLookup fields - Expr.NewTuple(exprs) - Array.mapi (fun i v -> - let expr = - if isNull v then simpleTypeExpr v - else - let tpy = v.GetType() - if tpy.IsArray then arrayExpr tpy v - elif FSharpType.IsTuple tpy then tupleExpr tpy v - elif FSharpType.IsUnion tpy then unionExpr v |> snd - elif FSharpType.IsRecord tpy then recordExpr v |> snd - else simpleTypeExpr v - Expr.Coerce(expr, fieldTypeLookup i) - ) fields |> List.ofArray - - and simpleTypeExpr instance = Expr.Value(instance) - - and unionExpr instance = - let caseInfo, fields = FSharpValue.GetUnionFields(instance, instance.GetType()) - let fieldInfo = caseInfo.GetFields() - let fieldTypeLookup indx = fieldInfo.[indx].PropertyType - caseInfo.DeclaringType, Expr.NewUnionCase(caseInfo, coerceValues fieldTypeLookup fields) - - and recordExpr instance = - let typ = instance.GetType() - let fields = FSharpValue.GetRecordFields(instance) - let fieldInfo = FSharpType.GetRecordFields(typ) - let fieldTypeLookup indx = fieldInfo.[indx].PropertyType - typ, Expr.NewRecord(instance.GetType(), coerceValues fieldTypeLookup fields) - - and arrayExpr (instance : 'a array) = - let typ = typeof<'a> - let arrayType = instance.GetType() - let exprs = coerceValues (fun _ -> typ) (instance |> Array.map box) - arrayType, Expr.NewArray(typ, exprs) - - let createLetExpr varType instance body args = - let var = Var("instance", varType) - Expr.Let(var, instance, body args (Expr.Var(var))) - - let quoteUnion instance = - let func instance = unionExpr instance ||> createLetExpr - Tracer.runAndMeasureExecutionTime "Quoted union type" (fun _ -> func instance) - - let quoteRecord instance = - let func instance = recordExpr instance ||> createLetExpr - Tracer.runAndMeasureExecutionTime "Quoted record type" (fun _ -> func instance) - - let quoteArray instance = - let func instance = arrayExpr instance ||> createLetExpr - Tracer.runAndMeasureExecutionTime "Quoted array type" (fun _ -> func instance) - -module internal ProvidedEnum = - let ctor = typeof.GetConstructors().[0] - - let makeProvidedType(name, items : string seq) = - let tdef = ProvidedTypeDefinition(name, Some typeof, nonNullable = true, isSealed = true) - tdef.AddMembersDelayed(fun _ -> - items - |> Seq.map (fun item -> - let getterCode (_ : Expr list) = - Expr.NewObject(ctor, [ <@@ name @@>; <@@ item @@> ]) - ProvidedProperty(item, tdef, getterCode, isStatic = true)) - |> Seq.cast - |> List.ofSeq) - tdef - -type internal ProvidedTypeMetadata = - { Name : string - Description : string option } - -module internal ProvidedInterface = - let makeProvidedType(metadata : ProvidedTypeMetadata) = - let tdef = ProvidedTypeDefinition("I" + metadata.Name.FirstCharUpper(), None, nonNullable = true, isInterface = true) - metadata.Description |> Option.iter tdef.AddXmlDoc - tdef - -type internal RecordPropertyMetadata = - { Name : string - Alias : string option - Description : string option - DeprecationReason : string option - Type : Type } - member x.AliasOrName = - match x.Alias with - | Some x -> x - | None -> x.Name - -type internal ProvidedRecordTypeDefinition(className, baseType) = - inherit ProvidedTypeDefinition(className, baseType, nonNullable = true) - - let mutable properties : RecordPropertyMetadata list = [] - - member __.GetRecordProperties() = properties - - member __.SetRecordProperties(props) = properties <- props - -[] -module internal Failures = - let uploadTypeIsNotScalar uploadTypeName = - failwithf "Upload type \"%s\" was found on the schema, but it is not a Scalar type. Upload types can only be used if they are defined as scalar types." uploadTypeName - -module internal ProvidedRecord = - let ctor = typeof.GetConstructors().[0] - - let makeProvidedType(tdef : ProvidedRecordTypeDefinition, properties : RecordPropertyMetadata list, explicitOptionalParameters: bool) = - let name = tdef.Name - tdef.AddMembersDelayed(fun _ -> - properties |> List.map (fun metadata -> - let pname = metadata.AliasOrName.FirstCharUpper() - let getterCode (args : Expr list) = - <@@ let this = %%args.[0] : RecordBase - match this.GetProperties() |> List.tryFind (fun prop -> prop.Name = pname) with - | Some prop -> prop.Value - | None -> failwithf "Expected to find property \"%s\", but the property was not found." pname @@> - let pdef = ProvidedProperty(pname, metadata.Type, getterCode) - metadata.Description |> Option.iter pdef.AddXmlDoc - metadata.DeprecationReason |> Option.iter pdef.AddObsoleteAttribute - pdef)) - let addConstructorDelayed (propertiesGetter : unit -> (string * string option * Type) list) = - tdef.AddMembersDelayed(fun _ -> - // We need to build a constructor that takes all optional properties wrapped in another option. - // We need to do this because optional parameters have issues with non-nullable types - // in the type provider SDK. They require a default value, and if the type is not nullable - // it provides the type default value for it. So basically, what's needed is three-valued behavior - // so we can differentiate between no value and null/None. - // - // I.e. to not send a value the optional parameter can either be set implicitly (by not providing an - // argument) or explicitly (`?parameterName = Some None` or `parameterName = None`). - // To set a value it has to be wrapped in an option: `parameterName = Some argumentValue` - // (or `?parameterName = Some (Some argumentValue)`). - // - // To keep backwards compatibility this constructor is only created if a flag is turned on. Otherwise - // we keep the previous behavior: We build a constructor overload for each optional property. - // Since RecordBase needs to know each property information in its own constructor, - // we also need to know each property that was not filled in the currently used overload. So we make - // combinations of all possible overloads, and for each one we map the user's provided values and - // fill the others with a null value. This way we can construct the RecordBase type providing all - // needed properties. - let properties = propertiesGetter() - let optionalProperties, requiredProperties = - properties - |> List.map (fun (name, alias, t) -> Option.defaultValue name alias, t) - |> List.partition (fun (_, t) -> isOption t) - if explicitOptionalParameters then - let constructorProperties = requiredProperties @ optionalProperties - let propertyNames = constructorProperties |> List.map (fst >> (fun x -> x.FirstCharUpper())) - let constructorPropertyTypes = constructorProperties |> List.map snd - let invoker (args : Expr list) = - let properties = - let baseConstructorArgs = - let coercedArgs = - (constructorPropertyTypes, args) - ||> List.map2 (fun t arg -> - let arg = Expr.Coerce(arg, typeof) - match t with - | Option (Option t) -> <@@ makeValue t %%arg @@> - | _ -> <@@ %%arg @@>) - (propertyNames, coercedArgs) - ||> List.map2 (fun name value -> <@@ { RecordProperty.Name = name; Value = %%value } @@>) - Expr.NewArray(typeof, baseConstructorArgs) - Expr.NewObject(ctor, [Expr.Value(name); properties]) - let constructorParams = - constructorProperties - |> List.map (fun (name, t) -> ProvidedParameter(name, t, ?optionalValue = if isOption t then Some null else None)) - [ProvidedConstructor(constructorParams, invoker)] - else - List.combinations optionalProperties - |> List.map (fun (optionalProperties, nullValuedProperties) -> - let constructorProperties = requiredProperties @ optionalProperties - let propertyNames = (constructorProperties @ nullValuedProperties) |> List.map (fst >> (fun x -> x.FirstCharUpper())) - let constructorPropertyTypes = constructorProperties |> List.map snd - let nullValuedPropertyTypes = nullValuedProperties |> List.map snd - let invoker (args : Expr list) = - let properties = - let baseConstructorArgs = - let coercedArgs = - (constructorPropertyTypes, args) - ||> List.map2 (fun t arg -> - let arg = Expr.Coerce(arg, typeof) - if isOption t then <@@ makeSome %%arg @@> else <@@ %%arg @@>) - let nullValuedArgs = nullValuedPropertyTypes |> List.map (fun _ -> <@@ null @@>) - (propertyNames, (coercedArgs @ nullValuedArgs)) - ||> List.map2 (fun name value -> <@@ { RecordProperty.Name = name; Value = %%value } @@>) - Expr.NewArray(typeof, baseConstructorArgs) - Expr.NewObject(ctor, [Expr.Value(name); properties]) - let constructorParams = - constructorProperties - |> List.map (fun (name, t) -> - match t with - | Option t -> ProvidedParameter(name, t) - | _ -> ProvidedParameter(name, t)) - ProvidedConstructor(constructorParams, invoker))) - match tdef.BaseType with - | :? ProvidedRecordTypeDefinition as bdef -> - bdef.AddMembersDelayed(fun _ -> - let asType = - let invoker (args : Expr list) = - <@@ let this = %%args.[0] : RecordBase - if this.GetName() = name then this - else failwithf "Expected type to be \"%s\", but it is \"%s\". Make sure to check the type by calling \"Is%s\" method before calling \"As%s\" method." name (this.GetName()) name name @@> - ProvidedMethod("As" + name, [], tdef, invoker) - let tryAsType = - let invoker (args : Expr list) = - <@@ let this = %%args.[0] : RecordBase - if this.GetName() = name then Some this - else None @@> - ProvidedMethod("TryAs" + name, [], typedefof<_ option>.MakeGenericType(tdef), invoker) - let isType = - let invoker (args : Expr list) = - <@@ let this = %%args.[0] : RecordBase - this.GetName() = name @@> - ProvidedMethod("Is" + name, [], typeof, invoker) - let members : MemberInfo list = [asType; tryAsType; isType] - members) - let propertiesGetter() = bdef.GetRecordProperties() @ properties |> List.map (fun p -> p.Name, p.Alias, p.Type) - addConstructorDelayed propertiesGetter - | _ -> - let propertiesGetter() = properties |> List.map (fun p -> p.Name, p.Alias, p.Type) - addConstructorDelayed propertiesGetter - tdef.SetRecordProperties(properties) - tdef - - let preBuildProvidedType(metadata : ProvidedTypeMetadata, baseType : Type option) = - let baseType = Option.defaultValue typeof baseType - let name = metadata.Name.FirstCharUpper() - let tdef = ProvidedRecordTypeDefinition(name, Some baseType) - tdef - -#nowarn "10001" - -module internal ProvidedOperationResult = - let makeProvidedType(operationType : Type) = - let tdef = ProvidedTypeDefinition("OperationResult", Some typeof, nonNullable = true) - tdef.AddMemberDelayed(fun _ -> - let getterCode (args : Expr list) = - <@@ let this = %%args.[0] : OperationResultBase - this.RawData @@> - let prop = ProvidedProperty("Data", operationType, getterCode) - prop.AddXmlDoc("Contains the data returned by the operation on the server.") - prop) - tdef - -module internal ProvidedOperation = - let makeProvidedType(actualQuery : string, - operationDefinition : OperationDefinition, - operationTypeName : string, - operationFieldsExpr : Expr, - schemaTypes: Map, - schemaProvidedTypes : Map, - operationType : Type, - contextInfo : GraphQLRuntimeContextInfo option, - uploadInputTypeName : string option, - className : string, - explicitOptionalParameters: bool) = - let tdef = ProvidedTypeDefinition(className, Some typeof) - tdef.AddXmlDoc("Represents a GraphQL operation on the server.") - tdef.AddMembersDelayed(fun _ -> - let operationResultDef = ProvidedOperationResult.makeProvidedType(operationType) - let isScalar (typeName: string) = - match schemaTypes.TryFind typeName with - | Some introspectionType -> introspectionType.Kind = TypeKind.SCALAR - | None -> false - let variables = - let rec mapVariable (variableName : string) (variableType : InputType) = - match variableType with - | NamedType typeName -> - match uploadInputTypeName with - | Some uploadInputTypeName when typeName = uploadInputTypeName -> - variableName, TypeMapping.makeOption typeof - | _ -> - match TypeMapping.scalar.TryFind(typeName) with - | Some t -> variableName, TypeMapping.makeOption t - | None when isScalar typeName -> variableName, typeof - | None -> - match schemaProvidedTypes.TryFind(typeName) with - | Some t -> variableName, TypeMapping.makeOption t - | None -> failwithf "Unable to find variable type \"%s\" in the schema definition." typeName - | ListType itype -> - let name, t = mapVariable variableName itype - name, t |> TypeMapping.makeArray |> TypeMapping.makeOption - | NonNullType itype -> - let name, t = mapVariable variableName itype - name, TypeMapping.unwrapOption t - operationDefinition.VariableDefinitions |> List.map (fun vdef -> mapVariable vdef.VariableName vdef.Type) - let buildVariablesExprFromArgs (varNames : string list) (args : Expr list) = - let mapVariableExpr (name : string) (value : Expr) = - let value = Expr.Coerce(value, typeof) - <@@ let rec mapVariableValue (value : obj) = - match value with - | null -> null - | :? string -> value // We need this because strings are enumerables, and we don't want to enumerate them recursively as an object - | :? EnumBase as v -> v.GetValue() |> box - | :? RecordBase as v -> v.ToDictionary() |> box - | OptionValue v -> v |> Option.map mapVariableValue |> box - | EnumerableValue v -> v |> Array.map mapVariableValue |> box - | v -> v - (name, mapVariableValue %%value) @@> - let args = - let varArgs = List.skip (args.Length - variables.Length) args - (varNames, varArgs) ||> List.map2 mapVariableExpr - Expr.NewArray(typeof, args) - let defaultContextExpr = - match contextInfo with - | Some info -> - let serverUrl = info.ServerUrl - let headerNames = info.HttpHeaders |> Seq.map fst |> Array.ofSeq - let headerValues = info.HttpHeaders |> Seq.map snd |> Array.ofSeq - <@@ { ServerUrl = serverUrl; HttpHeaders = Array.zip headerNames headerValues; Connection = new GraphQLClientConnection() } @@> - | None -> <@@ Unchecked.defaultof @@> - // We need to use the combination strategy to generate overloads for variables in the Run/AsyncRun methods. - // The strategy follows the same principle with ProvidedRecord constructor overloads, - // except that we also must create one additional overload for the runtime context, for each already existent overload, - // if no default context is provided. - let methodOverloadDefinitions = - let overloadsWithoutContext = - let optionalVariables, requiredVariables = - variables |> List.partition (fun (_, t) -> isOption t) - if explicitOptionalParameters then - [requiredVariables @ optionalVariables] - else - List.combinations optionalVariables - |> List.map (fun (optionalVariables, _) -> - let optionalVariables = optionalVariables |> List.map (fun (name, t) -> name, (TypeMapping.unwrapOption t)) - requiredVariables @ optionalVariables) - let overloadsWithContext = - overloadsWithoutContext - |> List.map (fun var -> ("runtimeContext", typeof) :: var) - match contextInfo with - | Some _ -> overloadsWithoutContext @ overloadsWithContext - | None -> overloadsWithContext - // Multipart requests should only be used when the user specifies a upload type name AND the type - // is present in the query as an input value. If not, we fallback to classic requests. - let shouldUseMultipartRequest = - let rec existsUploadType (foundTypes : ProvidedTypeDefinition list) (t : Type) = - match t with - | :? ProvidedTypeDefinition as tdef when not (List.contains tdef foundTypes) -> tdef.DeclaredProperties |> Seq.exists ((fun p -> p.PropertyType) >> existsUploadType (tdef :: foundTypes)) - | Option t -> existsUploadType foundTypes t - | Array t -> existsUploadType foundTypes t - | _ -> t = typeof - variables |> Seq.exists (snd >> existsUploadType []) - let runMethodOverloads : MemberInfo list = - let operationName = Option.toObj operationDefinition.Name - methodOverloadDefinitions |> List.map (fun overloadParameters -> - let variableNames = overloadParameters |> List.map fst |> List.filter (fun name -> name <> "runtimeContext") - let invoker (args : Expr list) = - // First arg is the operation instance, second should be the context, if the overload asks for one. - // We determine it by seeing if the variable names have one less item than the arguments without the instance. - let argsWithoutInstance = args.Tail - let variableArgs, isDefaultContext, context = - if argsWithoutInstance.Length - variableNames.Length = 1 - then argsWithoutInstance.Tail, false, argsWithoutInstance.Head - else argsWithoutInstance, true, defaultContextExpr - let variables = buildVariablesExprFromArgs variableNames variableArgs - let variables = - if explicitOptionalParameters then - <@@ (%%variables: (string * obj) []) - |> Array.filter (fun (_, value) -> - match value with - | :? Option as option -> option.IsSome - | _ -> true) @@> - else - variables - <@@ let context = %%context : GraphQLProviderRuntimeContext - let request = - { ServerUrl = context.ServerUrl - HttpHeaders = context.HttpHeaders - OperationName = Option.ofObj operationName - Query = actualQuery - Variables = %%variables } - let response = - if shouldUseMultipartRequest - then Tracer.runAndMeasureExecutionTime "Ran a multipart GraphQL query request" (fun _ -> GraphQLClient.sendMultipartRequest context.Connection request) - else Tracer.runAndMeasureExecutionTime "Ran a GraphQL query request" (fun _ -> GraphQLClient.sendRequest context.Connection request) - let responseJson = Tracer.runAndMeasureExecutionTime "Parsed a GraphQL response to a JsonValue" (fun _ -> JsonValue.Parse response) - // If the user does not provide a context, we should dispose the default one after running the query - if isDefaultContext then (context :> IDisposable).Dispose() - OperationResultBase(responseJson, %%operationFieldsExpr, operationTypeName) @@> - let methodParameters = overloadParameters |> List.map (fun (name, t) -> ProvidedParameter(name, t, ?optionalValue = if isOption t then Some null else None)) - let methodDef = ProvidedMethod("Run", methodParameters, operationResultDef, invoker) - methodDef.AddXmlDoc("Executes the operation on the server and fetch its results.") - upcast methodDef) - let asyncRunMethodOverloads : MemberInfo list = - let operationName = Option.toObj operationDefinition.Name - methodOverloadDefinitions |> List.map (fun overloadParameters -> - let variableNames = overloadParameters |> List.map fst |> List.filter (fun name -> name <> "runtimeContext") - let invoker (args : Expr list) = - // First arg is the operation instance, second should be the context, if the overload asks for one. - // We determine it by seeing if the variable names have one less item than the arguments without the instance. - let argsWithoutInstance = args.Tail - let variableArgs, isDefaultContext, context = - if argsWithoutInstance.Length - variableNames.Length = 1 - then argsWithoutInstance.Tail, false, argsWithoutInstance.Head - else argsWithoutInstance, true, defaultContextExpr - let variables = buildVariablesExprFromArgs variableNames variableArgs - let variables = - if explicitOptionalParameters then - <@@ (%%variables: (string * obj) []) - |> Array.filter (fun (_, value) -> - match value with - | :? Option as option -> option.IsSome - | _ -> true) @@> - else - variables - <@@ let context = %%context : GraphQLProviderRuntimeContext - let request = - { ServerUrl = context.ServerUrl - HttpHeaders = context.HttpHeaders - OperationName = Option.ofObj operationName - Query = actualQuery - Variables = %%variables } - async { - let! response = - if shouldUseMultipartRequest - then Tracer.asyncRunAndMeasureExecutionTime "Ran a multipart GraphQL query request asynchronously" (fun _ -> GraphQLClient.sendMultipartRequestAsync context.Connection request) - else Tracer.asyncRunAndMeasureExecutionTime "Ran a GraphQL query request asynchronously" (fun _ -> GraphQLClient.sendRequestAsync context.Connection request) - let responseJson = Tracer.runAndMeasureExecutionTime "Parsed a GraphQL response to a JsonValue" (fun _ -> JsonValue.Parse response) - // If the user does not provide a context, we should dispose the default one after running the query - if isDefaultContext then (context :> IDisposable).Dispose() - return OperationResultBase(responseJson, %%operationFieldsExpr, operationTypeName) - } @@> - let methodParameters = overloadParameters |> List.map (fun (name, t) -> ProvidedParameter(name, t, ?optionalValue = if isOption t then Some null else None)) - let methodDef = ProvidedMethod("AsyncRun", methodParameters, TypeMapping.makeAsync operationResultDef, invoker) - methodDef.AddXmlDoc("Executes the operation asynchronously on the server and fetch its results.") - upcast methodDef) - let parseResultDef = - let invoker (args : Expr list) = <@@ OperationResultBase(JsonValue.Parse %%args.[1], %%operationFieldsExpr, operationTypeName) @@> - let parameters = [ProvidedParameter("responseJson", typeof)] - let methodDef = ProvidedMethod("ParseResult", parameters, operationResultDef, invoker) - methodDef.AddXmlDoc("Parses a JSON response that matches the response pattern of the current operation into a OperationResult type.") - methodDef - let members : MemberInfo list = [operationResultDef; parseResultDef] @ runMethodOverloads @ asyncRunMethodOverloads - members) - tdef - -type internal ProvidedOperationMetadata = - { OperationType : Type - UploadInputTypeName : string option - TypeWrapper : ProvidedTypeDefinition } - -module internal Provider = - let getOperationMetadata (schemaTypes : Map, uploadInputTypeName : string option, enumProvidedTypes : Map, operationAstFields, operationTypeRef, explicitOptionalParameters: bool) = - let generateWrapper name = - let rec resolveWrapperName actual = - if schemaTypes.ContainsKey(actual) - then resolveWrapperName (actual + "Fields") - else actual - ProvidedTypeDefinition(resolveWrapperName name, None, isSealed = true) - let wrappersByPath = Dictionary() - let rootWrapper = generateWrapper "Types" - wrappersByPath.Add([], rootWrapper) - let rec getWrapper (path : string list) = - if wrappersByPath.ContainsKey path - then wrappersByPath.[path] - else - let wrapper = generateWrapper (path.Head.FirstCharUpper() + "Fields") - let upperWrapper = - let path = path.Tail - if wrappersByPath.ContainsKey(path) - then wrappersByPath.[path] - else getWrapper path - upperWrapper.AddMember(wrapper) - wrappersByPath.Add(path, wrapper) - wrapper - let includeType (path : string list) (t : ProvidedTypeDefinition) = - let wrapper = getWrapper path - wrapper.AddMember(t) - let providedTypes = Dictionary() - let rec getProvidedType (providedTypes : Dictionary) (schemaTypes : Map) (path : Path) (astFields : AstFieldInfo list) (tref : IntrospectionTypeRef) : Type = - match tref.Kind with - | TypeKind.SCALAR when tref.Name.IsSome -> TypeMapping.mapScalarType uploadInputTypeName tref.Name.Value |> TypeMapping.makeOption - | _ when uploadInputTypeName.IsSome && tref.Name.IsSome && uploadInputTypeName.Value = tref.Name.Value -> uploadTypeIsNotScalar uploadInputTypeName.Value - | TypeKind.NON_NULL when tref.Name.IsNone && tref.OfType.IsSome -> getProvidedType providedTypes schemaTypes path astFields tref.OfType.Value |> TypeMapping.unwrapOption - | TypeKind.LIST when tref.Name.IsNone && tref.OfType.IsSome -> getProvidedType providedTypes schemaTypes path astFields tref.OfType.Value |> TypeMapping.makeArray |> TypeMapping.makeOption - | TypeKind.ENUM when tref.Name.IsSome -> - match enumProvidedTypes.TryFind(tref.Name.Value) with - | Some providedEnum -> TypeMapping.makeOption providedEnum - | None -> failwithf "Could not find a enum type based on a type reference. The reference is an \"%s\" enum, but that enum was not found in the introspection schema." tref.Name.Value - | (TypeKind.OBJECT | TypeKind.INTERFACE | TypeKind.UNION) when tref.Name.IsSome -> - if providedTypes.ContainsKey(path, tref.Name.Value) - then TypeMapping.makeOption providedTypes.[path, tref.Name.Value] - else - let getIntrospectionFields typeName = - if schemaTypes.ContainsKey(typeName) - then schemaTypes.[typeName].Fields |> Option.defaultValue [||] - else failwithf "Could not find a schema type based on a type reference. The reference is to a \"%s\" type, but that type was not found in the schema types." typeName - let getPropertyMetadata typeName (info : AstFieldInfo) : RecordPropertyMetadata = - let ifield = - match getIntrospectionFields typeName |> Array.tryFind(fun f -> f.Name = info.Name) with - | Some ifield -> ifield - | None -> failwithf "Could not find field \"%s\" of type \"%s\". The schema type does not have a field with the specified name." info.Name tref.Name.Value - let path = info.AliasOrName :: path - let astFields = info.Fields - let ftype = getProvidedType providedTypes schemaTypes path astFields ifield.Type - { Name = info.Name; Alias = info.Alias; Description = ifield.Description; DeprecationReason = ifield.DeprecationReason; Type = ftype } - let fragmentProperties = - astFields - |> List.choose (function FragmentField f when f.TypeCondition <> tref.Name.Value -> Some f | _ -> None) - |> List.groupBy (fun field -> field.TypeCondition) - |> List.map (fun (typeCondition, fields) -> - let conditionFields = fields |> List.distinctBy (fun x -> x.AliasOrName) |> List.map FragmentField - typeCondition, List.map (getPropertyMetadata typeCondition) conditionFields) - let baseProperties = - astFields - |> List.choose (fun x -> - match x with - | TypeField _ -> Some x - | FragmentField f when f.TypeCondition = tref.Name.Value -> Some x - | _ -> None) - |> List.distinctBy (fun x -> x.AliasOrName) - |> List.map (getPropertyMetadata tref.Name.Value) - let baseType = - let metadata : ProvidedTypeMetadata = { Name = tref.Name.Value; Description = tref.Description } - let tdef = ProvidedRecord.preBuildProvidedType(metadata, None) - providedTypes.Add((path, tref.Name.Value), tdef) - includeType path tdef - ProvidedRecord.makeProvidedType(tdef, baseProperties, explicitOptionalParameters) - let createFragmentType (typeName, properties) = - let itype = - if schemaTypes.ContainsKey(typeName) - then schemaTypes.[typeName] - else failwithf "Could not find schema type based on the query. Type \"%s\" does not exist on the schema definition." typeName - let metadata : ProvidedTypeMetadata = { Name = itype.Name; Description = itype.Description } - let tdef = ProvidedRecord.preBuildProvidedType(metadata, Some (upcast baseType)) - providedTypes.Add((path, typeName), tdef) - includeType path tdef - ProvidedRecord.makeProvidedType(tdef, properties, explicitOptionalParameters) |> ignore - fragmentProperties |> List.iter createFragmentType - TypeMapping.makeOption baseType - | _ -> failwith "Could not find a schema type based on a type reference. The reference has an invalid or unsupported combination of Name, Kind and OfType fields." - let operationType = getProvidedType providedTypes schemaTypes [] operationAstFields operationTypeRef - { OperationType = operationType - UploadInputTypeName = uploadInputTypeName - TypeWrapper = rootWrapper } - - let getSchemaProvidedTypes(schema : IntrospectionSchema, uploadInputTypeName : string option, explicitOptionalParameters: bool) = - let providedTypes = ref Map.empty - let schemaTypes = TypeMapping.getSchemaTypes schema - let getSchemaType (tref : IntrospectionTypeRef) = - match tref.Name with - | Some name -> - match schemaTypes.TryFind(name) with - | Some itype -> itype - | None -> failwithf "Type \"%s\" was not found on the schema custom types." name - | None -> failwith "Expected schema type to have a name, but it does not have one." - let typeModifier (modifier : Type -> Type) (metadata : RecordPropertyMetadata) = { metadata with Type = modifier metadata.Type } - let makeOption = typeModifier TypeMapping.makeOption - let makeArrayOption = typeModifier (TypeMapping.makeArray >> TypeMapping.makeOption) - let unwrapOption = typeModifier TypeMapping.unwrapOption - let ofFieldType (field : IntrospectionField) = { field with Type = field.Type.OfType.Value } - let ofInputFieldType (field : IntrospectionInputVal) = { field with Type = field.Type.OfType.Value } - let rec resolveFieldMetadata (field : IntrospectionField) : RecordPropertyMetadata = - match field.Type.Kind with - | TypeKind.SCALAR when field.Type.Name.IsSome -> - let providedType = TypeMapping.mapScalarType uploadInputTypeName field.Type.Name.Value - { Name = field.Name - Alias = None - Description = field.Description - DeprecationReason = field.DeprecationReason - Type = providedType } - |> makeOption - | _ when uploadInputTypeName.IsSome && field.Type.Name.IsSome && uploadInputTypeName.Value = field.Type.Name.Value -> uploadTypeIsNotScalar uploadInputTypeName.Value - | TypeKind.NON_NULL when field.Type.Name.IsNone && field.Type.OfType.IsSome -> ofFieldType field |> resolveFieldMetadata |> unwrapOption - | TypeKind.LIST when field.Type.Name.IsNone && field.Type.OfType.IsSome -> ofFieldType field |> resolveFieldMetadata |> makeArrayOption - | (TypeKind.OBJECT | TypeKind.INTERFACE | TypeKind.INPUT_OBJECT | TypeKind.UNION | TypeKind.ENUM) when field.Type.Name.IsSome -> - let itype = getSchemaType field.Type - let providedType = resolveProvidedType itype - { Name = field.Name - Alias = None - Description = field.Description - DeprecationReason = field.DeprecationReason - Type = providedType } - |> makeOption - | _ -> failwith "Could not find a schema type based on a type reference. The reference has an invalid or unsupported combination of Name, Kind and OfType fields." - and resolveInputFieldMetadata (field : IntrospectionInputVal) : RecordPropertyMetadata = - match field.Type.Kind with - | TypeKind.SCALAR when field.Type.Name.IsSome -> - let providedType = TypeMapping.mapScalarType uploadInputTypeName field.Type.Name.Value - { Name = field.Name - Alias = None - Description = field.Description - DeprecationReason = None - Type = providedType } - |> makeOption - | _ when uploadInputTypeName.IsSome && field.Type.Name.IsSome && uploadInputTypeName.Value = field.Type.Name.Value -> uploadTypeIsNotScalar uploadInputTypeName.Value - | TypeKind.NON_NULL when field.Type.Name.IsNone && field.Type.OfType.IsSome -> ofInputFieldType field |> resolveInputFieldMetadata |> unwrapOption - | TypeKind.LIST when field.Type.Name.IsNone && field.Type.OfType.IsSome -> ofInputFieldType field |> resolveInputFieldMetadata |> makeArrayOption - | (TypeKind.OBJECT | TypeKind.INTERFACE | TypeKind.INPUT_OBJECT | TypeKind.UNION | TypeKind.ENUM) when field.Type.Name.IsSome -> - let itype = getSchemaType field.Type - let providedType = resolveProvidedType itype - { Name = field.Name - Alias = None - Description = field.Description - DeprecationReason = None - Type = providedType } - |> makeOption - | _ -> failwith "Could not find a schema type based on a type reference. The reference has an invalid or unsupported combination of Name, Kind and OfType fields." - and resolveProvidedType (itype : IntrospectionType) : ProvidedTypeDefinition = - if (!providedTypes).ContainsKey(itype.Name) - then (!providedTypes).[itype.Name] - else - let metadata = { Name = itype.Name; Description = itype.Description } - match itype.Kind with - | TypeKind.OBJECT -> - let tdef = ProvidedRecord.preBuildProvidedType(metadata, None) - providedTypes := (!providedTypes).Add(itype.Name, tdef) - let properties = - itype.Fields - |> Option.defaultValue [||] - |> Array.map resolveFieldMetadata - |> List.ofArray - upcast ProvidedRecord.makeProvidedType(tdef, properties, explicitOptionalParameters) - | TypeKind.INPUT_OBJECT -> - let tdef = ProvidedRecord.preBuildProvidedType(metadata, None) - providedTypes := (!providedTypes).Add(itype.Name, tdef) - let properties = - itype.InputFields - |> Option.defaultValue [||] - |> Array.map resolveInputFieldMetadata - |> List.ofArray - upcast ProvidedRecord.makeProvidedType(tdef, properties, explicitOptionalParameters) - | TypeKind.INTERFACE | TypeKind.UNION -> - let bdef = ProvidedInterface.makeProvidedType(metadata) - providedTypes := (!providedTypes).Add(itype.Name, bdef) - bdef - | TypeKind.ENUM -> - let items = - match itype.EnumValues with - | Some values -> values |> Array.map (fun value -> value.Name) - | None -> [||] - let tdef = ProvidedEnum.makeProvidedType(itype.Name, items) - providedTypes := (!providedTypes).Add(itype.Name, tdef) - tdef - | _ -> failwithf "Type \"%s\" is not a Record, Union, Enum, Input Object, or Interface type." itype.Name - let ignoredKinds = [TypeKind.SCALAR; TypeKind.LIST; TypeKind.NON_NULL] - schemaTypes |> Map.iter (fun _ itype -> if not (List.contains itype.Kind ignoredKinds) then resolveProvidedType itype |> ignore) - let possibleTypes (itype : IntrospectionType) = - match itype.PossibleTypes with - | Some trefs -> trefs |> Array.map (getSchemaType >> resolveProvidedType) - | None -> [||] - let getProvidedType typeName = - match (!providedTypes).TryFind(typeName) with - | Some ptype -> ptype - | None -> failwithf "Expected to find a type \"%s\" on the schema type map, but it was not found." typeName - schemaTypes - |> Seq.iter (fun kvp -> - if kvp.Value.Kind = TypeKind.INTERFACE || kvp.Value.Kind = TypeKind.UNION then - let itype = getProvidedType kvp.Value.Name - let ptypes = possibleTypes kvp.Value - ptypes |> Array.iter (fun ptype -> ptype.AddInterfaceImplementation(itype))) - !providedTypes - - let makeProvidedType(asm : Assembly, ns : string, resolutionFolder : string) = - let generator = ProvidedTypeDefinition(asm, ns, "GraphQLProvider", None) - let staticParams = - [ ProvidedStaticParameter("introspection", typeof) - ProvidedStaticParameter("httpHeaders", typeof, parameterDefaultValue = "") - ProvidedStaticParameter("resolutionFolder", typeof, parameterDefaultValue = resolutionFolder) - ProvidedStaticParameter("uploadInputTypeName", typeof, parameterDefaultValue = "") - ProvidedStaticParameter("clientQueryValidation", typeof, parameterDefaultValue = true) - ProvidedStaticParameter("explicitOptionalParameters", typeof, parameterDefaultValue = false) ] - generator.DefineStaticParameters(staticParams, fun tname args -> - let clientQueryValidation : bool = downcast args.[4] - let explicitOptionalParameters : bool = downcast args.[5] - let introspectionLocation = IntrospectionLocation.Create(downcast args.[0], downcast args.[2]) - let httpHeadersLocation = StringLocation.Create(downcast args.[1], resolutionFolder) - let uploadInputTypeName = - let name : string = unbox args.[3] - match name with - | null | "" -> None - | _ -> Some name - let maker = - lazy - let tdef = ProvidedTypeDefinition(asm, ns, tname, None) - tdef.AddXmlDoc("A type provider for GraphQL operations.") - tdef.AddMembersDelayed (fun _ -> - let httpHeaders = HttpHeaders.load httpHeadersLocation - let schemaJson = - match introspectionLocation with - | Uri serverUrl -> - use connection = new GraphQLClientConnection() - GraphQLClient.sendIntrospectionRequest connection serverUrl httpHeaders - | IntrospectionFile path -> - System.IO.File.ReadAllText path - let schema = Serialization.deserializeSchema schemaJson - - let schemaProvidedTypes = getSchemaProvidedTypes(schema, uploadInputTypeName, explicitOptionalParameters) - let typeWrapper = ProvidedTypeDefinition("Types", None, isSealed = true) - typeWrapper.AddMembers(schemaProvidedTypes |> Seq.map (fun kvp -> kvp.Value) |> List.ofSeq) - let operationWrapper = ProvidedTypeDefinition("Operations", None, isSealed = true) - let getContextMethodDef = - let methodParameters = - let serverUrl = - match introspectionLocation with - | Uri serverUrl -> ProvidedParameter("serverUrl", typeof, optionalValue = serverUrl) - | _ -> ProvidedParameter("serverUrl", typeof) - let httpHeaders = ProvidedParameter("httpHeaders", typeof>, optionalValue = null) - let connectionFactory = ProvidedParameter("connectionFactory", typeof GraphQLClientConnection>, optionalValue = null) - [serverUrl; httpHeaders; connectionFactory] - let defaultHttpHeadersExpr = - let names = httpHeaders |> Seq.map fst |> Array.ofSeq - let values = httpHeaders |> Seq.map snd |> Array.ofSeq - Expr.Coerce(<@@ Array.zip names values @@>, typeof>) - let invoker (args : Expr list) = - let serverUrl = args.[0] - <@@ let httpHeaders = - match %%args.[1] : seq with - | null -> %%defaultHttpHeadersExpr - | argHeaders -> argHeaders - let connectionFactory = - match %%args.[2] : unit -> GraphQLClientConnection with - | argHeaders when obj.Equals(argHeaders, null) -> fun () -> new GraphQLClientConnection() - | argHeaders -> argHeaders - { ServerUrl = %%serverUrl; HttpHeaders = httpHeaders; Connection = connectionFactory() } @@> - ProvidedMethod("GetContext", methodParameters, typeof, invoker, isStatic = true) - let operationMethodDef = - let staticParams = - [ ProvidedStaticParameter("query", typeof) - ProvidedStaticParameter("resolutionFolder", typeof, parameterDefaultValue = resolutionFolder) - ProvidedStaticParameter("operationName", typeof, parameterDefaultValue = "") - ProvidedStaticParameter("typeName", typeof, parameterDefaultValue = "") ] - let staticMethodDef = ProvidedMethod("Operation", [], typeof, isStatic = true) - let instanceBuilder (methodName : string) (args : obj []) = - let queryLocation = StringLocation.Create(downcast args.[0], downcast args.[1]) - let query = - match queryLocation with - | String query -> query - | File path -> System.IO.File.ReadAllText(path) - let queryAst = Parser.parse query - #if IS_DESIGNTIME - let throwExceptionIfValidationFailed (validationResult : ValidationResult) = - let rec formatValidationExceptionMessage(errors : AstError list) = - match errors with - | [] -> "Query validation resulted in invalid query, but no validation messages were produced." - | errors -> - errors - |> List.map (fun err -> - match err.Path with - | Some path -> sprintf "%s Path: %A" err.Message path - | None -> err.Message) - |> List.reduce (fun x y -> x + Environment.NewLine + y) - match validationResult with - | ValidationError msgs -> failwith (formatValidationExceptionMessage msgs) - | Success -> () - let key = { DocumentId = queryAst.GetHashCode(); SchemaId = schema.GetHashCode() } - let refMaker = lazy Validation.Ast.validateDocument schema queryAst - if clientQueryValidation then - refMaker.Force - |> QueryValidationDesignTimeCache.getOrAdd key - |> throwExceptionIfValidationFailed - #endif - let operationName : OperationName option = - match args.[2] :?> string with - | null | "" -> - let operationDefinitions = queryAst.Definitions |> List.filter (function OperationDefinition _ -> true | _ -> false) - match operationDefinitions with - | opdef :: _ -> opdef.Name - | _ -> failwith "Error parsing query. Can not choose a default operation: query document has no operation definitions." - | x -> Some x - let explicitOperationTypeName : TypeName option = - match args.[3] :?> string with - | null | "" -> None - | x -> Some x - let operationDefinition = - queryAst.Definitions - |> List.choose (function OperationDefinition odef -> Some odef | _ -> None) - |> List.find (fun d -> d.Name = operationName) - let operationAstFields = - let infoMap = queryAst.GetInfoMap() - match infoMap.TryFind(operationName) with - | Some fields -> fields - | None -> failwith "Error parsing query. Could not find field information for requested operation." - let operationTypeRef = - let tref = - match operationDefinition.OperationType with - | Query -> schema.QueryType - | Mutation -> - match schema.MutationType with - | Some tref -> tref - | None -> failwith "The operation is a mutation operation, but the schema does not have a mutation type." - | Subscription -> - match schema.SubscriptionType with - | Some tref -> tref - | None -> failwithf "The operation is a subscription operation, but the schema does not have a subscription type." - let tinst = - match tref.Name with - | Some name -> schema.Types |> Array.tryFind (fun t -> t.Name = name) - | None -> None - match tinst with - | Some t -> { tref with Kind = t.Kind } - | None -> failwith "The operation was found in the schema, but it does not have a name." - let schemaTypes = TypeMapping.getSchemaTypes schema - let enumProvidedTypes = schemaProvidedTypes |> Map.filter (fun _ t -> t.BaseType = typeof) - let actualQuery = queryAst.ToQueryString(QueryStringPrintingOptions.IncludeTypeNames).Replace("\r\n", "\n") - let className = - match explicitOperationTypeName, operationDefinition.Name with - | Some name, _ -> name.FirstCharUpper() - | None, Some name -> name.FirstCharUpper() - | None, None -> "Operation" + actualQuery.MD5Hash() - let metadata = getOperationMetadata(schemaTypes, uploadInputTypeName, enumProvidedTypes, operationAstFields, operationTypeRef, explicitOptionalParameters) - let operationTypeName : TypeName = - match operationTypeRef.Name with - | Some name -> name - | None -> failwith "Error parsing query. Operation type does not have a name." - let rec getKind (tref : IntrospectionTypeRef) = - match tref.Kind with - | TypeKind.NON_NULL | TypeKind.LIST when tref.OfType.IsSome -> getKind tref.OfType.Value - | _ -> tref.Kind - let rec getTypeName (tref : IntrospectionTypeRef) = - match tref.Kind with - | TypeKind.NON_NULL | TypeKind.LIST when tref.OfType.IsSome -> getTypeName tref.OfType.Value - | _ -> - match tref.Name with - | Some tname -> tname - | None -> failwithf "Expected type kind \"%s\" to have a name, but it does not have a name." (tref.Kind.ToString()) - let rec getIntrospectionType (tref : IntrospectionTypeRef) = - match tref.Kind with - | TypeKind.NON_NULL | TypeKind.LIST when tref.OfType.IsSome -> getIntrospectionType tref.OfType.Value - | _ -> - let typeName = getTypeName tref - match schemaTypes.TryFind(typeName) with - | Some t -> t - | None -> failwithf "Type \"%s\" was not found in the introspection schema." typeName - let getOperationFields (operationAstFields : AstFieldInfo list) (operationType : IntrospectionType) = - let rec helper (acc : SchemaFieldInfo list) (astFields : AstFieldInfo list) (introspectionType : IntrospectionType) = - match introspectionType.Kind with - | TypeKind.OBJECT | TypeKind.INTERFACE | TypeKind.UNION -> - match astFields with - | [] -> acc - | field :: tail -> - let throw typeName = failwithf "Field \"%s\" of type \"%s\" was not found in the introspection schema." field.Name typeName - let tref = - match field with - | FragmentField fragf -> - let fragmentType = - let tref = - Option.defaultValue [||] introspectionType.PossibleTypes - |> Array.map getIntrospectionType - |> Array.append [|introspectionType|] - |> Array.tryFind (fun pt -> pt.Name = fragf.TypeCondition) - match tref with - | Some t -> t - | None -> failwithf "Fragment field defines a type condition \"%s\", but that type was not found in the schema definition." fragf.TypeCondition - let field = - fragmentType.Fields - |> Option.map (Array.tryFind (fun f -> f.Name = fragf.Name)) - |> Option.flatten - match field with - | Some f -> f.Type - | None -> throw fragmentType.Name - | TypeField typef -> - let field = - introspectionType.Fields - |> Option.map (Array.tryFind (fun f -> f.Name = typef.Name)) - |> Option.flatten - match field with - | Some f -> f.Type - | None -> throw introspectionType.Name - let fields = - match getKind tref with - | TypeKind.OBJECT | TypeKind.INTERFACE | TypeKind.UNION -> - let schemaType = getIntrospectionType tref - helper [] field.Fields schemaType - | _ -> [] - let info = { AliasOrName = field.AliasOrName.FirstCharUpper(); SchemaTypeRef = tref; Fields = Array.ofList fields } - helper (info :: acc) tail introspectionType - | _ -> [] - helper [] operationAstFields operationType |> Array.ofList - - // Every time we run the query, we will need the schema types information as an expression. - // To avoid creating the type map expression every time we call Run method, we cache it here. - let operationFieldsExpr = getOperationFields operationAstFields (getIntrospectionType operationTypeRef) |> QuotationHelpers.arrayExpr |> snd - let contextInfo : GraphQLRuntimeContextInfo option = - match introspectionLocation with - | Uri serverUrl -> Some { ServerUrl = serverUrl; HttpHeaders = httpHeaders } - | _ -> None - let operationDef = ProvidedOperation.makeProvidedType(actualQuery, operationDefinition, operationTypeName, operationFieldsExpr, schemaTypes, schemaProvidedTypes, metadata.OperationType, contextInfo, metadata.UploadInputTypeName, className, explicitOptionalParameters) - operationDef.AddMember(metadata.TypeWrapper) - let invoker (_ : Expr list) = <@@ OperationBase(query) @@> - let methodDef = ProvidedMethod(methodName, [], operationDef, invoker, isStatic = true) - methodDef.AddXmlDoc("Creates an operation to be executed on the server and provide its return types.") - operationWrapper.AddMember(operationDef) - operationDef.AddMember(methodDef) - methodDef - staticMethodDef.DefineStaticParameters(staticParams, instanceBuilder) - staticMethodDef - let schemaPropertyDef = - let getter = QuotationHelpers.quoteRecord schema (fun (_ : Expr list) schema -> schema) - ProvidedProperty("Schema", typeof, getter, isStatic = true) - let members : MemberInfo list = [typeWrapper; operationWrapper; getContextMethodDef; operationMethodDef; schemaPropertyDef] - members) - tdef - #if IS_DESIGNTIME - let providerKey = - { IntrospectionLocation = introspectionLocation - CustomHttpHeadersLocation = httpHeadersLocation - UploadInputTypeName = uploadInputTypeName - ResolutionFolder = resolutionFolder - ClientQueryValidation = clientQueryValidation - ExplicitOptionalParameters = explicitOptionalParameters } - ProviderDesignTimeCache.getOrAdd providerKey maker.Force) - #else - maker.Force()) - #endif - generator diff --git a/src/FSharp.Data.GraphQL.Client.DesignTime/Provider.fs b/src/FSharp.Data.GraphQL.Client.DesignTime/Provider.fs new file mode 100644 index 000000000..f2d540e2c --- /dev/null +++ b/src/FSharp.Data.GraphQL.Client.DesignTime/Provider.fs @@ -0,0 +1,235 @@ +/// The MIT License (MIT) +/// Copyright (c) 2016 Bazinga Technologies Inc + +module FSharp.Data.GraphQL.Provider + +open System +open System.IO +open System.Collections.Generic +open System.Text.RegularExpressions +open FSharp.Core +open System.Reflection +open FSharp.Data.GraphQL +open FSharp.Data.GraphQL.Client +open FSharp.Data.GraphQL.Ast +open FSharp.Data.GraphQL.Types.Introspection +open ProviderImplementation.ProvidedTypes +open FSharp.Quotations + +#nowarn "10001" + +let private loadSchema (introspectionLocation: IntrospectionLocation) (httpHeaders: seq)= + let schemaJsonStream: Stream = + match introspectionLocation with + | Uri serverUrl -> + use connection = new GraphQLClientConnection() + GraphQLClient.sendIntrospectionRequest connection serverUrl httpHeaders + | IntrospectionFile path -> + System.IO.File.OpenRead(path) :> _ + + Serialization.deserializeSchema schemaJsonStream + +let importsRegex = Regex(@"^\s*#import\s+(?.*)", RegexOptions.Compiled ||| RegexOptions.Multiline) +let parseImports (query: string) = + let matches = importsRegex.Matches(query) + [ for import in matches do + let file = import.Groups.["file"] + file.Value.Trim(' ', '\'', '"') ] + +let loadFragmentFromPath (file: string) = + if File.Exists file then + let content = File.ReadAllText file + let imports = parseImports content + let document = Parser.parse content + imports, document + else + failwithf "Fragment file does not exist on disk %s" file + +type ProvidedFragmentMetadata = unit + +let rec buildFragmentType (selectionSet: Selection list) (fragment: ProvidedTypeDefinition) (parent: ProvidedTypeDefinition) = + for selection in selectionSet do + match selection with + | InlineFragment ast -> + match ast.TypeCondition with + | Some typeCondition -> + let subFragmentInterface = ProvidedTypeDefinition(typeCondition, Some(fragment :> _)) + fragment.AddMember subFragmentInterface + | None -> + () + | Field field -> + () + //fragmentInterface.AddMember(ProvidedProperty("Foo", typeof, isStatic=false)) + //.AddMember(ProvidedProperty(property.Name, property.PropertyType)) + | FragmentSpread fragmentSpread -> + () + parent.AddMember(fragment) + +let rec makeFragmentInterface + (file: string) (schema: IntrospectionSchema) + (schemaProvidedTypes: Map) + (fragmentWrapper: ProvidedTypeDefinition) + (cache: Map) = + let imports, document = loadFragmentFromPath file + [ for node in document.Definitions do + match node with + | FragmentDefinition definition -> + match definition.Name, definition.TypeCondition with + | Some name, Some typeCondition -> + let fragmentType = ProvidedTypeDefinition(name, None, isSealed=false) + let providedType = + match schemaProvidedTypes.TryFind typeCondition with + | Some providedType -> providedType + | None -> failwithf "Fragment \"%s\" defined in \"%s\" refers to a type \"%s\" that does not exist in the introspection schema." name file typeCondition + () + | _, _ -> + () + | OperationDefinition op -> + () + ] + +let private makeFragmentInterfaces + (schema: IntrospectionSchema) + (schemaProvidedTypes: Map) + (fragmentWrapper: ProvidedTypeDefinition) + (providerSettings: ProviderSettings) = + match providerSettings.FragmentsFolder with + | Some folder -> + let fragmentFiles = Directory.GetFiles(folder, "*.graphql") + for file in fragmentFiles do + makeFragmentInterface file schema schemaProvidedTypes fragmentWrapper Map.empty |> ignore + | None -> + () + +let private makeOperationMethodDef (providerSettings: ProviderSettings) (schemaGenerator: SchemaGenerator) + (httpHeaders: seq) (operationWrapper: ProvidedTypeDefinition) = + let staticParams = + [ ProvidedStaticParameter("query", typeof) + ProvidedStaticParameter("resolutionFolder", typeof, parameterDefaultValue = providerSettings.ResolutionFolder) + ProvidedStaticParameter("operationName", typeof, parameterDefaultValue = "") + ProvidedStaticParameter("typeName", typeof, parameterDefaultValue = "") ] + let staticMethodDef = ProvidedMethod("Operation", [], typeof, isStatic = true) + let instanceBuilder (methodName : string) (args : obj []) = + let opGen = OperationGenerator(providerSettings, schemaGenerator, httpHeaders, operationWrapper) + let queryOrPath = + match args.[0] with + | :? string as s -> s + | _ -> invalidArg "query" "Invalid queryString argument" + let resolutionFolder = + match args.[1] with + | :? string as s -> s + | _ -> providerSettings.ResolutionFolder + let operationName = + match args.[2] with + | :? string as s when not(String.IsNullOrEmpty s) -> Some s + | _ -> None + let typeName = + match args.[2] with + | :? string as s when not(String.IsNullOrEmpty s) -> Some s + | _ -> None + let op = opGen.Generate(queryOrPath, resolutionFolder, ?operationName=operationName, ?typeName=typeName) + let query = op.Query + let invoker (_ : Expr list) = <@@ OperationBase(query) @@> + let methodDef = ProvidedMethod(methodName, [], op.OperationType, invoker, isStatic = true) + methodDef.AddXmlDoc("Creates an operation to be executed on the server and provide its return types.") + operationWrapper.AddMember(op.OperationType) + op.OperationType.AddMember(methodDef) + methodDef + staticMethodDef.DefineStaticParameters(staticParams, instanceBuilder) + staticMethodDef + + +let private makeRootType (asm: Assembly) (ns: string) (tname: string) (providerSettings: ProviderSettings) = + let tdef = ProvidedTypeDefinition(asm, ns, tname, Some typeof) + tdef.AddXmlDoc("A type provider for GraphQL operations.") + tdef.AddMembersDelayed (fun () -> + let httpHeaders = HttpHeaders.load providerSettings.CustomHttpHeadersLocation + let schema = loadSchema providerSettings.IntrospectionLocation httpHeaders + let schemaGen = SchemaGenerator(schema, + ?uploadInputTypeName = providerSettings.UploadInputTypeName, + explicitOptionalParameters = providerSettings.ExplicitOptionalParameters) + let schemaProvidedTypes = schemaGen.ProvidedTypes + let schemaProvidedTypesList = schemaProvidedTypes |> Seq.map (fun kvp -> kvp.Value) |> List.ofSeq + let typeWrapper = ProvidedTypeDefinition("Types", Some typeof, isSealed = true) + typeWrapper.AddMembers schemaProvidedTypesList + + + let fragmentsWrapper = ProvidedTypeDefinition("Fragments", None, isSealed = true) + //makeFragmentInterfaces schema schemaProvidedTypes fragmentsWrapper providerSettings + + let operationWrapper = ProvidedTypeDefinition("Operations", None, isSealed = true) + let getContextMethodDef = + let methodParameters = + let serverUrl = + match providerSettings.IntrospectionLocation with + | Uri serverUrl -> ProvidedParameter("serverUrl", typeof, optionalValue = serverUrl) + | _ -> ProvidedParameter("serverUrl", typeof) + let httpHeaders = ProvidedParameter("httpHeaders", typeof>, optionalValue = null) + let connectionFactory = ProvidedParameter("connectionFactory", typeof GraphQLClientConnection>, optionalValue = null) + [serverUrl; httpHeaders; connectionFactory] + let defaultHttpHeadersExpr = + let names = httpHeaders |> Seq.map fst |> Array.ofSeq + let values = httpHeaders |> Seq.map snd |> Array.ofSeq + Expr.Coerce(<@@ Array.zip names values @@>, typeof>) + let invoker (args : Expr list) = + let serverUrl = args.[0] + <@@ let httpHeaders = + match %%args.[1] : seq with + | null -> %%defaultHttpHeadersExpr + | argHeaders -> argHeaders + let connectionFactory = + match %%args.[2] : unit -> GraphQLClientConnection with + | argHeaders when obj.Equals(argHeaders, null) -> fun () -> new GraphQLClientConnection() + | argHeaders -> argHeaders + { ServerUrl = %%serverUrl; HttpHeaders = httpHeaders; Connection = connectionFactory() } @@> + ProvidedMethod("GetContext", methodParameters, typeof, invoker, isStatic = true) + let operationMethodDef = makeOperationMethodDef providerSettings schemaGen httpHeaders operationWrapper + let schemaPropertyDef = + let getter = QuotationHelpers.quoteRecord schema (fun (_ : Expr list) schema -> schema) + ProvidedProperty("Schema", typeof, getter, isStatic = true) + let members : MemberInfo list = [typeWrapper; operationWrapper; fragmentsWrapper; getContextMethodDef; operationMethodDef; schemaPropertyDef] + members) + tdef + +let makeGraphQLProvider(asm : Assembly, ns : string, resolutionFolder : string) = + let generator = ProvidedTypeDefinition(asm, ns, "GraphQLProvider", None) + let staticParams = + [ ProvidedStaticParameter("introspection", typeof) + ProvidedStaticParameter("httpHeaders", typeof, parameterDefaultValue = "") + ProvidedStaticParameter("resolutionFolder", typeof, parameterDefaultValue = resolutionFolder) + ProvidedStaticParameter("uploadInputTypeName", typeof, parameterDefaultValue = "") + ProvidedStaticParameter("clientQueryValidation", typeof, parameterDefaultValue = true) + ProvidedStaticParameter("explicitOptionalParameters", typeof, parameterDefaultValue = false) + ProvidedStaticParameter("fragmentsFolder", typeof, parameterDefaultValue="") ] + generator.DefineStaticParameters(staticParams, fun tname args -> + let clientQueryValidation : bool = downcast args.[4] + let explicitOptionalParameters : bool = downcast args.[5] + let introspectionLocation = IntrospectionLocation.Create(downcast args.[0], downcast args.[2]) + let httpHeadersLocation = StringLocation.Create(downcast args.[1], resolutionFolder) + let uploadInputTypeName = + match args.[3] with + | :? string as name + when not(String.IsNullOrEmpty name) -> Some name + | _ -> None + let fragmentsFolder = + match args.[6] with + | :? string as folder + when not(String.IsNullOrEmpty folder) -> + Some(Path.Combine(resolutionFolder, folder)) + | _ -> None + let providerSettings = + { IntrospectionLocation = introspectionLocation + CustomHttpHeadersLocation = httpHeadersLocation + UploadInputTypeName = uploadInputTypeName + ResolutionFolder = resolutionFolder + ClientQueryValidation = clientQueryValidation + ExplicitOptionalParameters = explicitOptionalParameters + FragmentsFolder = fragmentsFolder } + let maker = lazy makeRootType asm ns tname providerSettings +#if IS_DESIGNTIME + ProviderDesignTimeCache.getOrAdd providerSettings maker.Force +#else + maker.Force() +#endif + ) + generator \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Client.DesignTime/SchemaGenerator.fs b/src/FSharp.Data.GraphQL.Client.DesignTime/SchemaGenerator.fs new file mode 100644 index 000000000..6213be9e7 --- /dev/null +++ b/src/FSharp.Data.GraphQL.Client.DesignTime/SchemaGenerator.fs @@ -0,0 +1,121 @@ +namespace FSharp.Data.GraphQL + +open System +open System.Collections.Generic +open FSharp.Core +open FSharp.Data.GraphQL +open FSharp.Data.GraphQL.Types.Introspection +open FSharp.Data.GraphQL.Client +open FSharp.Data.GraphQL.ProvidedSchema +open ProviderImplementation.ProvidedTypes + +type internal SchemaGenerator (schema: IntrospectionSchema, ?uploadInputTypeName: string, ?explicitOptionalParameters: bool) = + let explicitOptionalParameters = defaultArg explicitOptionalParameters false + let schemaTypes = SchemaTypes(schema, ?uploadInputTypeName=uploadInputTypeName) + let providedTypes = Dictionary() + let rec resolveFieldMetadata (field : IntrospectionField) : RecordPropertyMetadata = + let providedType = resolveByTypeRef field.Type false + { Name = field.Name + Alias = None + Description = field.Description + DeprecationReason = field.DeprecationReason + Type = providedType } + and resolveInputFieldMetadata (field : IntrospectionInputVal) : RecordPropertyMetadata = + let providedType = resolveByTypeRef field.Type false + { Name = field.Name + Alias = None + Description = field.Description + DeprecationReason = None + Type = providedType } + and resolveByTypeRef (typeRef: IntrospectionTypeRef) (isNonNullable: bool) = + match typeRef with + | NamedTypeRef(TypeKind.SCALAR, name) -> + let scalar = TypeMapping.mapScalarType uploadInputTypeName name + if isNonNullable + then scalar + else TypeMapping.makeOption scalar + | NamedTypeRef(_, name) -> + let introspectionType = schemaTypes.FindType name + let providedType = resolveProvidedType introspectionType :> Type + if isNonNullable + then providedType + else TypeMapping.makeOption providedType + | ContainerTypeRef(TypeKind.LIST, innerTypeRef) -> + let innerType = resolveByTypeRef innerTypeRef false + let listType = TypeMapping.makeArray innerType + if isNonNullable + then listType + else TypeMapping.makeOption listType + | ContainerTypeRef(TypeKind.NON_NULL, innerTypeRef) -> + resolveByTypeRef innerTypeRef true + | typeRef -> + failwithf "Unsupported Type ref in introspection Schema '%A'" typeRef.Name + and resolveProvidedType (itype : IntrospectionType) : ProvidedTypeDefinition = + if providedTypes.ContainsKey(itype.Name) + then providedTypes.[itype.Name] + else + let metadata = { Name = itype.Name; Description = itype.Description } + match itype.Kind with + | TypeKind.OBJECT -> + let tdef = preBuildProvidedRecordType(metadata, None) + providedTypes.Add(itype.Name, tdef) + let properties = + itype.Fields + |> Option.defaultValue [||] + |> Array.map resolveFieldMetadata + |> List.ofArray + upcast makeProvidedRecordType(tdef, properties, explicitOptionalParameters) + | TypeKind.INPUT_OBJECT -> + let tdef = preBuildProvidedRecordType(metadata, None) + providedTypes.Add(itype.Name, tdef) + let properties = + itype.InputFields + |> Option.defaultValue [||] + |> Array.map resolveInputFieldMetadata + |> List.ofArray + upcast makeProvidedRecordType(tdef, properties, explicitOptionalParameters) + | TypeKind.INTERFACE | TypeKind.UNION -> + let bdef = makeProvidedInterfaceType(metadata) + providedTypes.Add(itype.Name, bdef) + bdef + | TypeKind.ENUM -> + let items = + match itype.EnumValues with + | Some values -> values |> Array.map (fun value -> value.Name) + | None -> [||] + let tdef = makeProvidedEnumType(itype.Name, items) + providedTypes.Add(itype.Name, tdef) + tdef + | _ -> failwithf "Type \"%s\" is not a Record, Union, Enum, Input Object, or Interface type." itype.Name + + do + for KeyValue(_, schemaType) in schemaTypes.Types do + let ignoredType = + schemaType.Kind = TypeKind.NON_NULL || + schemaType.Kind = TypeKind.SCALAR || + schemaType.Kind = TypeKind.LIST + if not ignoredType then + resolveProvidedType schemaType |> ignore + + let isAbstract = + schemaType.Kind = TypeKind.INTERFACE || + schemaType.Kind = TypeKind.UNION + if isAbstract then + let itype = + match providedTypes.TryGetValue(schemaType.Name) with + | true, ptype -> ptype + | false, _-> failwithf "Type \"%s\" does not exist in the schema." schemaType.Name + let possibleTypes = + match schemaType.PossibleTypes with + | Some trefs -> trefs |> Array.map (schemaTypes.FindByTypeRef >> resolveProvidedType) + | None -> [||] + for possibleType in possibleTypes do + possibleType.AddInterfaceImplementation(itype) + let enumProvidedTypes = + dict [ for KeyValue(name, providedType) in providedTypes do + if providedType.BaseType = typeof then + name, providedType ] + + member _.SchemaTypes = schemaTypes + member _.ProvidedTypes = providedTypes :> IDictionary<_,_> + member _.EnumProvidedTypes = enumProvidedTypes \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Client.DesignTime/paket.references b/src/FSharp.Data.GraphQL.Client.DesignTime/paket.references index 91d46b834..a045557a0 100644 --- a/src/FSharp.Data.GraphQL.Client.DesignTime/paket.references +++ b/src/FSharp.Data.GraphQL.Client.DesignTime/paket.references @@ -1,5 +1,6 @@ group Common FSharp.Core +System.Text.Json FParsec Microsoft.Extensions.Http File:ProvidedTypes.fsi diff --git a/src/FSharp.Data.GraphQL.Client/BaseTypes.fs b/src/FSharp.Data.GraphQL.Client/BaseTypes.fs deleted file mode 100644 index 251f329ad..000000000 --- a/src/FSharp.Data.GraphQL.Client/BaseTypes.fs +++ /dev/null @@ -1,444 +0,0 @@ -/// The MIT License (MIT) -/// Copyright (c) 2016 Bazinga Technologies Inc - -namespace FSharp.Data.GraphQL - -open System -open System.Globalization -open FSharp.Core -open FSharp.Data.GraphQL.Client -open FSharp.Data.GraphQL.Client.ReflectionPatterns -open FSharp.Data.GraphQL.Types.Introspection -open System.Text -open System.ComponentModel - -/// Contains information about a field on the query. -type SchemaFieldInfo = - /// Gets the alias or the name of the field. - { AliasOrName : string - /// Gets the introspection type information of the field. - SchemaTypeRef : IntrospectionTypeRef - /// Gets information about fields of this field, if it is an object type. - Fields : SchemaFieldInfo [] } - -/// A type alias to represent a Type name. -type TypeName = string - -/// Contains data about a GQL operation error. -type OperationError = - /// The description of the error that happened in the operation. - { Message : string - /// The path to the field that produced the error while resolving its value. - Path : obj [] } - -/// Contains helpers to build HTTP header sequences to be used in GraphQLProvider Run methods. -module HttpHeaders = - /// Builds a sequence of HTTP headers as a sequence from a pre-formatted header string. - /// The input headers string should be a string containing headers in the same way they are - /// organized in a HTTP request (each header in a line, names and values separated by commas). - let ofString (headers : string) : seq = - upcast (headers.Replace("\r\n", "\n").Split('\n') - |> Array.map (fun header -> - let separatorIndex = header.IndexOf(':') - if separatorIndex = -1 - then failwithf "Header \"%s\" has an invalid header format. Must provide a name and a value, both separated by a comma." header - else - let name = header.Substring(0, separatorIndex).Trim() - let value = header.Substring(separatorIndex + 1).Trim() - (name, value))) - - /// Builds a sequence of HTTP headers as a sequence from a header file. - /// The input file should be a file containing headers in the same way they are - /// organized in a HTTP request (each header in a line, names and values separated by commas). - let ofFile (path : string) = - System.IO.File.ReadAllText path |> ofString - - let internal load (location : StringLocation) : seq = - let headersString = - match location with - | String headers -> headers - | File path -> System.IO.File.ReadAllText path - if headersString = "" then upcast [||] - else headersString |> ofString - -/// The base type for all GraphQLProvider provided enum types. -type EnumBase (name : string, value : string) = - /// Gets the name of the provided enum type. - member __.GetName() = name - - /// Gets the value of the provided enum type. - member __.GetValue() = value - - override x.ToString() = x.GetValue() - - member x.Equals(other : EnumBase) = - x.GetName() = other.GetName() && x.GetValue() = other.GetValue() - - override x.Equals(other : obj) = - match other with - | :? EnumBase as other -> x.Equals(other) - | _ -> false - - override x.GetHashCode() = x.GetName().GetHashCode() ^^^ x.GetValue().GetHashCode() - - interface IEquatable with - member x.Equals(other) = x.Equals(other) - -/// Contains information about a GraphQLProvider record property. -type RecordProperty = - /// Gets the name of the record property. - { Name : string - /// Gets the value of the record property. - Value : obj } - -/// The base type for all GraphQLProvider provided record types. -type RecordBase (name : string, properties : RecordProperty seq) = - do - if not (isNull properties) - then - let distinctCount = properties |> Seq.map (fun p -> p.Name) |> Seq.distinct |> Seq.length - if distinctCount <> Seq.length properties - then failwith "Duplicated property names were found. Record can not be created, because each property name must be distinct." - - let properties = - if not (isNull properties) - then properties |> Seq.sortBy (fun x -> x.Name) |> List.ofSeq - else [] - - /// Gets the name of this provided record type. - member __.GetName() = name - - /// Gets a list of this provided record properties. - member __.GetProperties() = properties - - /// Produces a dictionary containing all the properties of this provided record type. - member x.ToDictionary() = - let rec mapDictionaryValue (v : obj) = - match v with - | null -> null - | :? string -> v // We need this because strings are enumerables, and we don't want to enumerate them recursively as an object - | :? EnumBase as v -> v.GetValue() |> box - | :? RecordBase as v -> box (v.ToDictionary()) - | OptionValue v -> v |> Option.map mapDictionaryValue |> Option.toObj - | EnumerableValue v -> v |> Array.map mapDictionaryValue |> box - | _ -> v - x.GetProperties() - |> Seq.choose (fun p -> - if not (isNull p.Value) - then Some (p.Name, mapDictionaryValue p.Value) - else None) - |> dict - - override x.ToString() = - let getPropValue (prop : RecordProperty) = sprintf "%A" prop.Value - let sb = StringBuilder() - sb.Append("{") |> ignore - let rec printProperties (properties : RecordProperty list) = - match properties with - | [] -> () - | [prop] -> sb.Append(sprintf "%s = %s;" prop.Name (getPropValue prop)) |> ignore - | prop :: tail -> sb.AppendLine(sprintf "%s = %s;" prop.Name (getPropValue prop)) |> ignore; printProperties tail - printProperties (x.GetProperties()) - sb.Append("}") |> ignore - sb.ToString() - - member x.Equals(other : RecordBase) = - x.GetName() = other.GetName() && x.GetProperties() = other.GetProperties() - - override x.Equals(other : obj) = - match other with - | :? RecordBase as other -> x.Equals(other) - | _ -> false - - override x.GetHashCode() = - x.GetName().GetHashCode() ^^^ x.GetProperties().GetHashCode() - - interface IEquatable with - member x.Equals(other) = x.Equals(other) - -module internal TypeMapping = - let scalar = - [| "Int", typeof - "Boolean", typeof - "Date", typeof - "Float", typeof - "ID", typeof - "String", typeof - "URI", typeof |] - |> Map.ofArray - - let getSchemaTypes (introspection : IntrospectionSchema) = - let schemaTypeNames = - [| "__TypeKind" - "__DirectiveLocation" - "__Type" - "__InputValue" - "__Field" - "__EnumValue" - "__Directive" - "__Schema" |] - let isScalarType (name : string) = - scalar |> Map.containsKey name - let isIntrospectionType (name : string) = - schemaTypeNames |> Array.contains name - introspection.Types - |> Array.choose (fun t -> - if not (isIntrospectionType t.Name) && not (isScalarType t.Name) - then Some(t.Name, t) - else None) - |> Map.ofArray - - let mapScalarType uploadInputTypeName tname = - match uploadInputTypeName with - | Some uploadInputTypeName when uploadInputTypeName = tname -> typeof - | _ -> - // Unknown scalar types will be mapped to a string type. - if scalar.ContainsKey(tname) - then scalar.[tname] - else typeof - - let makeOption (t : Type) = typedefof<_ option>.MakeGenericType(t) - - let makeArray (t : Type) = t.MakeArrayType() - - let unwrapOption (t : Type) = - if t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<_ option> - then t.GetGenericArguments().[0] - else failwithf "Expected type to be an Option type, but it is %s." t.Name - - let makeAsync (t : Type) = typedefof>.MakeGenericType(t) - -module internal JsonValueHelper = - let getResponseFields (responseJson : JsonValue) = - match responseJson with - | JsonValue.Record fields -> fields - | _ -> failwithf "Expected root type to be a Record type, but type is %A." responseJson - - let getResponseDataFields (responseJson : JsonValue) = - match getResponseFields responseJson |> Array.tryFind (fun (name, _) -> name = "data") with - | Some (_, data) -> - match data with - | JsonValue.Record fields -> Some fields - | JsonValue.Null -> None - | _ -> failwithf "Expected data field of root type to be a Record type, but type is %A." data - | None -> None - - let getResponseErrors (responseJson : JsonValue) = - match getResponseFields responseJson |> Array.tryFind (fun (name, _) -> name = "errors") with - | Some (_, errors) -> - match errors with - | JsonValue.Array [||] | JsonValue.Null -> None - | JsonValue.Array items -> Some items - | _ -> failwithf "Expected error field of root type to be an Array type, but type is %A." errors - | None -> None - - let getResponseCustomFields (responseJson : JsonValue) = - getResponseFields responseJson - |> Array.filter (fun (name, _) -> name <> "data" && name <> "errors") - - let private removeTypeNameField (fields : (string * JsonValue) []) = - fields |> Array.filter (fun (name, _) -> name <> "__typename") - - let firstUpper (name : string, value) = - name.FirstCharUpper(), value - - let getTypeName (fields : (string * JsonValue) seq) = - fields - |> Seq.tryFind (fun (name, _) -> name = "__typename") - |> Option.map (fun (_, value) -> - match value with - | JsonValue.String x -> x - | _ -> failwithf "Expected \"__typename\" field to be a string field, but it was %A." value) - - let rec getFieldValue (schemaField : SchemaFieldInfo) (fieldName : string, fieldValue : JsonValue) = - let getScalarType (typeRef : IntrospectionTypeRef) = - let getType (typeName : string) = - match Map.tryFind typeName TypeMapping.scalar with - | Some t -> t - | None -> typeof - match typeRef.Name with - | Some name -> getType name - | None -> failwith "Expected scalar type to have a name, but it does not have one." - let rec helper (useOption : bool) (schemaField : SchemaFieldInfo) (fieldValue : JsonValue) : obj = - let makeSomeIfNeeded value = - match schemaField.SchemaTypeRef.Kind with - | TypeKind.NON_NULL -> value - | _ when useOption -> makeSome value - | _ -> value - let makeNoneIfNeeded (t : Type) = - match schemaField.SchemaTypeRef.Kind with - | TypeKind.NON_NULL -> null - | _ when useOption -> makeNone t - | _ -> null - match fieldValue with - | JsonValue.Array items -> - let items = - let itemType = - let tref = - match schemaField.SchemaTypeRef.Kind with - | TypeKind.LIST -> schemaField.SchemaTypeRef.OfType - | TypeKind.NON_NULL -> - match schemaField.SchemaTypeRef.OfType with - | Some t when t.Kind = TypeKind.LIST -> t.OfType - | _ -> failwithf "Expected field to be a list type with an underlying item, but it is %A." schemaField.SchemaTypeRef.OfType - | _ -> failwithf "Expected field to be a list type with an underlying item, but it is %A." schemaField.SchemaTypeRef - match tref with - | Some t -> t - | None -> failwith "Schema type is a list type, but no underlying type was specified." - let items = - let schemaField = { schemaField with SchemaTypeRef = itemType } - items |> Array.map (helper false schemaField) - match itemType.Kind with - | TypeKind.NON_NULL -> - match itemType.OfType with - | Some itemType -> - match itemType.Kind with - | TypeKind.NON_NULL -> failwith "Schema definition is not supported: a non null type of a non null type was specified." - | TypeKind.OBJECT | TypeKind.INTERFACE | TypeKind.UNION -> makeArray typeof items - | TypeKind.ENUM -> makeArray typeof items - | TypeKind.SCALAR -> makeArray (getScalarType itemType) items - | kind -> failwithf "Unsupported type kind \"%A\"." kind - | None -> failwith "Item type is a non null type, but no underlying type exists on the schema definition of the type." - | TypeKind.OBJECT | TypeKind.INTERFACE | TypeKind.UNION -> makeOptionArray typeof items - | TypeKind.ENUM -> makeOptionArray typeof items - | TypeKind.SCALAR -> makeOptionArray (getScalarType itemType) items - | kind -> failwithf "Unsupported type kind \"%A\"." kind - makeSomeIfNeeded items - | JsonValue.Record props -> - let typeName = - match getTypeName props with - | Some typeName -> typeName - | None -> failwith "Expected type to have a \"__typename\" field, but it was not found." - let mapRecordProperty (aliasOrName : string, value : JsonValue) = - let schemaField = - match schemaField.Fields |> Array.tryFind (fun f -> f.AliasOrName = aliasOrName) with - | Some f -> f - | None -> failwithf "Expected to find field information for field with alias or name \"%s\" of type \"%s\" but it was not found." aliasOrName typeName - let value = helper true schemaField value - { Name = aliasOrName; Value = value } - let props = - props - |> removeTypeNameField - |> Array.map (firstUpper >> mapRecordProperty) - RecordBase(typeName, props) |> makeSomeIfNeeded - | JsonValue.Boolean b -> makeSomeIfNeeded b - | JsonValue.Float f -> makeSomeIfNeeded f - | JsonValue.Null -> - match schemaField.SchemaTypeRef.Kind with - | TypeKind.NON_NULL -> failwith "Expected a non null item from the schema definition, but a null item was found in the response." - | TypeKind.OBJECT | TypeKind.INTERFACE | TypeKind.UNION -> makeNoneIfNeeded typeof - | TypeKind.ENUM -> makeNoneIfNeeded typeof - | TypeKind.SCALAR -> getScalarType schemaField.SchemaTypeRef |> makeNoneIfNeeded - | TypeKind.LIST -> null - | kind -> failwithf "Unsupported type kind \"%A\"." kind - | JsonValue.Integer n -> makeSomeIfNeeded n - | JsonValue.String s -> - match schemaField.SchemaTypeRef.Kind with - | TypeKind.NON_NULL -> - match schemaField.SchemaTypeRef.OfType with - | Some itemType -> - match itemType.Kind with - | TypeKind.NON_NULL -> failwith "Schema definition is not supported: a non null type of a non null type was specified." - | TypeKind.SCALAR -> - match itemType.Name with - | Some "URI" -> - System.Uri(s) |> box - | Some "Date" -> - match DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None) with - | (true, d) -> box d - | _ -> failwith "A string was received in the query response, and the schema recognizes it as a date and time sring, but the conversion failed." - | Some _ -> - box s - | _ -> failwith "A string type was received in the query response item, but the matching schema field is not a string based type." - | TypeKind.ENUM when itemType.Name.IsSome -> EnumBase(itemType.Name.Value, s) |> box - | _ -> failwith "A string type was received in the query response item, but the matching schema field is not a string or an enum type." - | None -> failwith "Item type is a non null type, but no underlying type exists on the schema definition of the type." - | TypeKind.SCALAR -> - match schemaField.SchemaTypeRef.Name with - | Some "URI" -> - System.Uri(s) |> makeSomeIfNeeded - | Some "Date" -> - match DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None) with - | (true, d) -> makeSomeIfNeeded d - | _ -> failwith "A string was received in the query response, and the schema recognizes it as a date and time sring, but the conversion failed." - | Some _ -> - makeSomeIfNeeded s - | _ -> failwith "A string type was received in the query response item, but the matching schema field is not a string based type." - | TypeKind.ENUM when schemaField.SchemaTypeRef.Name.IsSome -> EnumBase(schemaField.SchemaTypeRef.Name.Value, s) |> makeSomeIfNeeded - | _ -> failwith "A string type was received in the query response item, but the matching schema field is not a string based type or an enum type." - fieldName, (helper true schemaField fieldValue) - - let getFieldValues (schemaTypeName : string) (schemaFields : SchemaFieldInfo []) (dataFields : (string * JsonValue) []) = - let mapFieldValue (aliasOrName : string, value : JsonValue) = - let schemaField = - match schemaFields |> Array.tryFind (fun f -> f.AliasOrName = aliasOrName) with - | Some f -> f - | None -> failwithf "Expected to find field information for field with alias or name \"%s\" of type \"%s\" but it was not found." aliasOrName schemaTypeName - getFieldValue schemaField (aliasOrName, value) - removeTypeNameField dataFields - |> Array.map (firstUpper >> mapFieldValue) - - let getErrors (errors : JsonValue []) = - let errorMapper = function - | JsonValue.Record fields -> - match fields |> Array.tryFind (fun (name, _) -> name = "message"), fields |> Array.tryFind (fun (name, _) -> name = "path") with - | Some (_, JsonValue.String message), Some (_, JsonValue.Array path) -> - let pathMapper = function - | JsonValue.String x -> box x - | JsonValue.Integer x -> box x - | _ -> failwith "Error parsing response errors. A item in the path is neither a String or a Number." - { Message = message; Path = Array.map pathMapper path } - | Some (_, JsonValue.String message), None-> - { Message = message; Path = [||]} - | _ -> failwith "Error parsing response errors. Unsupported errors field format." - | other -> failwithf "Error parsing response errors. Expected error to be a Record type, but it is %s." (other.ToString()) - Array.map errorMapper errors - -/// The base type for all GraphQLProvider operation result provided types. -type OperationResultBase (responseJson : JsonValue, operationFields : SchemaFieldInfo [], operationTypeName : string) = - let rawData = - let data = JsonValueHelper.getResponseDataFields responseJson - match data with - | Some [||] | None -> None - | Some dataFields -> - let fieldValues = JsonValueHelper.getFieldValues operationTypeName operationFields dataFields - let props = fieldValues |> Array.map (fun (name, value) -> { Name = name; Value = value }) - Some (RecordBase(operationTypeName, props)) - - let errors = - let errors = JsonValueHelper.getResponseErrors responseJson - match errors with - | None -> [||] - | Some errors -> JsonValueHelper.getErrors errors - - let customData = - JsonValueHelper.getResponseCustomFields responseJson - |> Serialization.deserializeMap - - member private __.ResponseJson = responseJson - - /// - [] - [] - member __.RawData = rawData - - /// Gets all the errors returned by the operation on the server. - member __.Errors = errors - - /// Gets all the custom data returned by the operation on server as a map of names and values. - member __.CustomData = customData - - member x.Equals(other : OperationResultBase) = - x.ResponseJson = other.ResponseJson - - override x.Equals(other : obj) = - match other with - | :? OperationResultBase as other -> x.Equals(other) - | _ -> false - - override x.GetHashCode() = x.ResponseJson.GetHashCode() - -/// The base type for al GraphQLProvider operation provided types. -type OperationBase (query : string) = - /// Gets the query string of the operation. - member __.Query = query \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Client/Extensions.fs b/src/FSharp.Data.GraphQL.Client/Extensions.fs index 413138d62..a4607f69c 100644 --- a/src/FSharp.Data.GraphQL.Client/Extensions.fs +++ b/src/FSharp.Data.GraphQL.Client/Extensions.fs @@ -4,28 +4,76 @@ namespace FSharp.Data.GraphQL.Client open System +open System.Collections open System.Collections.Generic open System.Text +open System.Globalization +open System.Text.RegularExpressions +open System.Threading open System.Security.Cryptography +module internal Casing = + let private nonWordChars = Regex(@"[^a-zA-Z0-9]+", RegexOptions.Compiled) + let private word = Regex( @"^(?\d+|^[a-z]+|[A-Z]+|[A-Z][a-z]+|\d[a-z]+)+$", RegexOptions.Compiled) + let toTileCase (text: string) (textInfo: TextInfo) = + let result = StringBuilder() + let tokens = nonWordChars.Split(text) + for token in tokens do + let wordMatch = word.Match(token) + let groups = wordMatch.Groups.["word"] + for capture in groups.Captures do + result.Append(textInfo.ToTitleCase(capture.Value.ToString())) |> ignore + result.ToString() + /// Extensions for types used by the GraphQL client library. [] module internal Extensions = type String with - /// Returns the input string with the first character in upper case. - member this.FirstCharUpper() = - this.Substring(0, 1).ToUpperInvariant() + this.Substring(1) - - /// Returns the input string with the first character in lower case. - member this.FirstCharLower() = - this.Substring(0, 1).ToLowerInvariant() + this.Substring(1) - member this.MD5Hash() = Encoding.UTF8.GetBytes(this) |> MD5.Create().ComputeHash |> Array.map (fun x -> x.ToString("x2")) |> Array.reduce (+) + member this.ToTitleCase(?cultureInfo: CultureInfo) = + let culture = defaultArg cultureInfo Thread.CurrentThread.CurrentCulture + let textInfo = culture.TextInfo + Casing.toTileCase this textInfo + +module internal Printing = + let prettyPrintRecord (record: seq>) = + let builder = StringBuilder() + let newLine indentation plus = + builder.AppendLine() |> ignore + builder.Append(' ', indentation + plus) |> ignore + let rec print indentation (value: obj) = + match value with + | :? seq> as r -> + builder.Append("{") |> ignore + for i, KeyValue(key, value) in Seq.indexed r do + if i > 0 then + builder.Append(";") |> ignore + newLine indentation 2 + builder.Append(key.ToTitleCase() + " = ") |> ignore + print (indentation + 2) value |> ignore + builder.Append(" }") + | :? string as s -> + builder.Append(sprintf "%A" s) + | :? IEnumerable as e -> + let values = e |> Seq.cast |> Array.ofSeq + builder.Append("[") |> ignore + for i = 0 to values.Length - 1 do + if i > 0 then builder.Append(";") |> ignore + newLine indentation 2 + print (indentation + 2) values.[i] |> ignore + if values.Length > 0 then newLine indentation 0 + builder.Append("]") + | value -> + builder.Append(sprintf "%A" value) + let builder = print 0 record + builder.ToString() + + /// Basic operations on lists. module internal List = /// Produces all possible combination sets for an input list, with all possible sizes. diff --git a/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj b/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj index 042050af4..369026f9e 100644 --- a/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj +++ b/src/FSharp.Data.GraphQL.Client/FSharp.Data.GraphQL.Client.fsproj @@ -11,15 +11,13 @@ - - + - - + diff --git a/src/FSharp.Data.GraphQL.Client/GraphQLClient.fs b/src/FSharp.Data.GraphQL.Client/GraphQLClient.fs index a5b504159..61564cd70 100644 --- a/src/FSharp.Data.GraphQL.Client/GraphQLClient.fs +++ b/src/FSharp.Data.GraphQL.Client/GraphQLClient.fs @@ -4,13 +4,16 @@ namespace FSharp.Data.GraphQL open System +open System.IO open System.Collections.Generic +open System.Threading +open System.Text +open System.Collections open System.Net.Http +open FSharp.Reflection open FSharp.Data.GraphQL open FSharp.Data.GraphQL.Client -open System.Text -open ReflectionPatterns -open System.Threading + /// A requrest object for making GraphQL calls using the GraphQL client module. type GraphQLRequest = @@ -25,8 +28,39 @@ type GraphQLRequest = /// Gets variables to be sent with the query. Variables : (string * obj) [] } + /// Executes calls to GraphQL servers and return their responses. module GraphQLClient = + + let rec private mapUploadVariables (accum: (string * Upload) list) (path: string) (key: string) (value : obj) = + match value with + | :? string | null -> + accum + | :? Upload as x -> + (path + "." + key, x)::accum + | :? RecordBase as x -> + (accum, x.GetProperties()) + ||> Seq.fold(fun accum value -> mapUploadVariables accum (path + "." + key) value.Key value.Value) + | :? IEnumerable as enumerable -> + let values = Seq.cast enumerable + ((0, accum), values) + ||> Seq.fold(fun (idx, accum) item -> + idx + 1, mapUploadVariables accum (path + "." + key) (string idx) item) + |> snd + | x -> + let xtype = x.GetType() + if xtype.IsGenericType && xtype.GetGenericTypeDefinition() = typedefof> then + match FSharpValue.GetUnionFields(value, xtype) with + | _, [| innerValue |] -> mapUploadVariables accum path key innerValue + | _, _ -> accum + else + accum + + let extractFileUploadMapping (request: GraphQLRequest) = + request.Variables + |> Seq.collect(fun (k,v) -> mapUploadVariables [] "variables" k v) + |> Seq.toArray + let private ensureSuccessCode (response : Async) = async { let! response = response @@ -43,7 +77,7 @@ module GraphQLClient = requestMessage.Content <- content addHeaders httpHeaders requestMessage let! response = invoker.SendAsync(requestMessage, CancellationToken.None) |> Async.AwaitTask |> ensureSuccessCode - let! content = response.Content.ReadAsStringAsync() |> Async.AwaitTask + let! content = response.Content.ReadAsStreamAsync() |> Async.AwaitTask return content } @@ -51,28 +85,26 @@ module GraphQLClient = async { use requestMessage = new HttpRequestMessage(HttpMethod.Get, serverUrl) let! response = invoker.SendAsync(requestMessage, CancellationToken.None) |> Async.AwaitTask |> ensureSuccessCode - let! content = response.Content.ReadAsStringAsync() |> Async.AwaitTask + let! content = response.Content.ReadAsStreamAsync() |> Async.AwaitTask return content } + let private createStreamContent (data: 'T) = async { + let memoryStream = new MemoryStream() + do! Serialization.serializeToStream data memoryStream + let content = new StreamContent(memoryStream) + return content + } /// Sends a request to a GraphQL server asynchronously. let sendRequestAsync (connection : GraphQLClientConnection) (request : GraphQLRequest) = async { let invoker = connection.Invoker - let variables = - match request.Variables with - | null | [||] -> JsonValue.Null - | _ -> Map.ofArray request.Variables |> Serialization.toJsonValue - let operationName = - match request.OperationName with - | Some x -> JsonValue.String x - | None -> JsonValue.Null let requestJson = - [| "operationName", operationName - "query", JsonValue.String request.Query - "variables", variables |] - |> JsonValue.Record - let content = new StringContent(requestJson.ToString(), Encoding.UTF8, "application/json") + {| operationName = request.OperationName + query = request.Query + variables = dict request.Variables |} + let! content = createStreamContent requestJson + content.Headers.Add("Content-Type", "application/json") return! postAsync invoker request.ServerUrl request.HttpHeaders content } @@ -112,64 +144,35 @@ module GraphQLClient = sendIntrospectionRequestAsync client serverUrl httpHeaders |> Async.RunSynchronously + /// Executes a multipart request to a GraphQL server asynchronously. let sendMultipartRequestAsync (connection : GraphQLClientConnection) (request : GraphQLRequest) = async { let invoker = connection.Invoker let boundary = "----GraphQLProviderBoundary" + (Guid.NewGuid().ToString("N")) let content = new MultipartContent("form-data", boundary) - let files = - let rec tryMapFileVariable (name: string, value : obj) = - match value with - | null | :? string -> None - | :? Upload as x -> Some [|name, x|] - | OptionValue x -> - x |> Option.bind (fun x -> tryMapFileVariable (name, x)) - | :? IDictionary as x -> - x |> Seq.collect (fun kvp -> tryMapFileVariable (name + "." + (kvp.Key.FirstCharLower()), kvp.Value) |> Option.defaultValue [||]) - |> Array.ofSeq - |> Some - | EnumerableValue x -> - x |> Array.mapi (fun ix x -> tryMapFileVariable (name + "." + (ix.ToString()), x)) - |> Array.collect (Option.defaultValue [||]) - |> Some - | _ -> None - request.Variables |> Array.collect (tryMapFileVariable >> (Option.defaultValue [||])) - let operationContent = - let variables = - match request.Variables with - | null | [||] -> JsonValue.Null - | _ -> request.Variables |> Map.ofArray |> Serialization.toJsonValue - let operationName = - match request.OperationName with - | Some x -> JsonValue.String x - | None -> JsonValue.Null - let json = - [| "operationName", operationName - "query", JsonValue.String request.Query - "variables", variables |] - |> JsonValue.Record - let content = new StringContent(json.ToString(JsonSaveOptions.DisableFormatting)) - content.Headers.Add("Content-Disposition", "form-data; name=\"operations\"") - content + let files = extractFileUploadMapping request + let operationJson = + {| operationName = request.OperationName + query = request.Query + variables = dict request.Variables |} + let! operationContent = createStreamContent operationJson + operationContent.Headers.Add("Content-Disposition", "form-data; name=\"operations\"") content.Add(operationContent) - let mapContent = - let files = - files - |> Array.mapi (fun ix (name, _) -> ix.ToString(), JsonValue.Array [| JsonValue.String ("variables." + name) |]) - |> JsonValue.Record - let content = new StringContent(files.ToString(JsonSaveOptions.DisableFormatting)) - content.Headers.Add("Content-Disposition", "form-data; name=\"map\"") - content - content.Add(mapContent) - let fileContents = + let filesMapJson = files - |> Array.mapi (fun ix (_, value) -> - let content = new StreamContent(value.Stream) - content.Headers.Add("Content-Disposition", sprintf "form-data; name=\"%i\"; filename=\"%s\"" ix value.FileName) - content.Headers.Add("Content-Type", value.ContentType) - content) - fileContents |> Array.iter content.Add + |> Array.mapi(fun i (key, v) -> (i.ToString()), [key]) + |> dict + let! mapContent = createStreamContent filesMapJson + mapContent.Headers.Add("Content-Disposition", "form-data; name=\"map\"") + content.Add(mapContent) + + for (i, (_, upload)) in Array.indexed files do + let uploadContent = new StreamContent(upload.Stream) + uploadContent.Headers.Add("Content-Disposition", sprintf "form-data; name=\"%i\"; filename=\"%s\"" i upload.FileName) + uploadContent.Headers.Add("Content-Type", upload.ContentType) + content.Add uploadContent + let! result = postAsync invoker request.ServerUrl request.HttpHeaders content return result } diff --git a/src/FSharp.Data.GraphQL.Client/HttpHeaders.fs b/src/FSharp.Data.GraphQL.Client/HttpHeaders.fs new file mode 100644 index 000000000..196880fed --- /dev/null +++ b/src/FSharp.Data.GraphQL.Client/HttpHeaders.fs @@ -0,0 +1,37 @@ +/// The MIT License (MIT) +/// Copyright (c) 2016 Bazinga Technologies Inc + +namespace FSharp.Data.GraphQL + +open FSharp.Core +open FSharp.Data.GraphQL.Client + +/// Contains helpers to build HTTP header sequences to be used in GraphQLProvider Run methods. +module HttpHeaders = + /// Builds a sequence of HTTP headers as a sequence from a pre-formatted header string. + /// The input headers string should be a string containing headers in the same way they are + /// organized in a HTTP request (each header in a line, names and values separated by commas). + let ofString (headers : string) : seq = + upcast (headers.Replace("\r\n", "\n").Split('\n') + |> Array.map (fun header -> + let separatorIndex = header.IndexOf(':') + if separatorIndex = -1 + then failwithf "Header \"%s\" has an invalid header format. Must provide a name and a value, both separated by a comma." header + else + let name = header.Substring(0, separatorIndex).Trim() + let value = header.Substring(separatorIndex + 1).Trim() + (name, value))) + + /// Builds a sequence of HTTP headers as a sequence from a header file. + /// The input file should be a file containing headers in the same way they are + /// organized in a HTTP request (each header in a line, names and values separated by commas). + let ofFile (path : string) = + System.IO.File.ReadAllText path |> ofString + + let internal load (location : StringLocation) : seq = + let headersString = + match location with + | String headers -> headers + | File path -> System.IO.File.ReadAllText path + if headersString = "" then upcast [||] + else headersString |> ofString \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Client/JsonValue.fs b/src/FSharp.Data.GraphQL.Client/JsonValue.fs deleted file mode 100644 index 0ef13881a..000000000 --- a/src/FSharp.Data.GraphQL.Client/JsonValue.fs +++ /dev/null @@ -1,293 +0,0 @@ -// -------------------------------------------------------------------------------------- -// Copyright (c) Microsoft Corporation 2005-2012. -// This sample code is provided "as is" without warranty of any kind. -// We disclaim all warranties, either express or implied, including the -// warranties of merchantability and fitness for a particular purpose. -// -// A simple F# portable parser for JSON data -// -------------------------------------------------------------------------------------- - -/// The MIT License (MIT) -/// Copyright (c) 2016 Bazinga Technologies Inc - -namespace FSharp.Data.GraphQL.Client - -open System -open System.IO -open System.ComponentModel -open System.Globalization -open System.Text - -/// Specifies the formatting behaviour of JSON values. -[] -type JsonSaveOptions = - /// Format (indent) the JsonValue. - | None = 0 - /// Print the JsonValue in one line in a compact way. - | DisableFormatting = 1 - -/// Represents a JSON value. Large numbers that do not fit in the -/// Decimal type are represented using the Float case, while -/// smaller numbers are represented as decimals to avoid precision loss. -[] -[] -type JsonValue = - | Integer of int - | String of string - | Float of float - | Record of properties:(string * JsonValue)[] - | Array of elements:JsonValue[] - | Boolean of bool - | Null - - /// - [] - [] - member x._Print = - let str = x.ToString() - if str.Length > 512 then str.Substring(0, 509) + "..." - else str - - /// Serializes the JsonValue to the specified System.IO.TextWriter. - member x.WriteTo (w:TextWriter, saveOptions) = - let newLine = - if saveOptions = JsonSaveOptions.None then - fun indentation plus -> - w.WriteLine() - System.String(' ', indentation + plus) |> w.Write - else - fun _ _ -> () - let propSep = - if saveOptions = JsonSaveOptions.None then "\": " - else "\":" - let rec serialize indentation = function - | Null -> w.Write "null" - | Boolean b -> w.Write(if b then "true" else "false") - | Float number -> w.Write number - | Integer number -> w.Write number - | String s -> - w.Write "\"" - JsonValue.JsonStringEncodeTo w s - w.Write "\"" - | Record properties -> - w.Write "{" - for i = 0 to properties.Length - 1 do - let k,v = properties.[i] - if i > 0 then w.Write "," - newLine indentation 2 - w.Write "\"" - JsonValue.JsonStringEncodeTo w k - w.Write propSep - serialize (indentation + 2) v - newLine indentation 0 - w.Write "}" - | Array elements -> - w.Write "[" - for i = 0 to elements.Length - 1 do - if i > 0 then w.Write "," - newLine indentation 2 - serialize (indentation + 2) elements.[i] - if elements.Length > 0 then - newLine indentation 0 - w.Write "]" - serialize 0 x - - static member internal JsonStringEncodeTo (w:TextWriter) (value:string) = - if String.IsNullOrEmpty value then () - else - for i = 0 to value.Length - 1 do - let c = value.[i] - let ci = int c - if ci >= 0 && ci <= 7 || ci = 11 || ci >= 14 && ci <= 31 then - w.Write("\\u{0:x4}", ci) |> ignore - else - match c with - | '\b' -> w.Write "\\b" - | '\t' -> w.Write "\\t" - | '\n' -> w.Write "\\n" - | '\f' -> w.Write "\\f" - | '\r' -> w.Write "\\r" - | '"' -> w.Write "\\\"" - | '\\' -> w.Write "\\\\" - | _ -> w.Write c - - member x.ToString saveOptions = - let w = new StringWriter(CultureInfo.InvariantCulture) - x.WriteTo(w, saveOptions) - w.GetStringBuilder().ToString() - - override x.ToString() = x.ToString(JsonSaveOptions.None) - -type private JsonParser(jsonText:string) = - let mutable i = 0 - let s = jsonText - let buf = StringBuilder() - - let skipWhitespace() = - while i < s.Length && Char.IsWhiteSpace s.[i] do - i <- i + 1 - let isNumChar c = - Char.IsDigit c || c = '.' || c='e' || c='E' || c='+' || c='-' - let isIntChar c = - Char.IsDigit c || c = '+' || c = '-' - let throw() = - let msg = - sprintf - "Invalid JSON starting at character %d, snippet = \n----\n%s\n-----\njson = \n------\n%s\n-------" - i (jsonText.[(max 0 (i-10))..(min (jsonText.Length-1) (i+10))]) (if jsonText.Length > 1000 then jsonText.Substring(0, 1000) else jsonText) - failwith msg - let ensure cond = - if not cond then throw() - - let rec parseValue() = - skipWhitespace() - ensure(i < s.Length) - match s.[i] with - | '"' -> JsonValue.String(parseString()) - | '-' -> parseNum() - | c when Char.IsDigit(c) -> parseNum() - | '{' -> parseObject() - | '[' -> parseArray() - | 't' -> parseLiteral("true", JsonValue.Boolean true) - | 'f' -> parseLiteral("false", JsonValue.Boolean false) - | 'n' -> parseLiteral("null", JsonValue.Null) - | _ -> throw() - - and parseRootValue() = - skipWhitespace() - ensure(i < s.Length) - match s.[i] with - | '{' -> parseObject() - | '[' -> parseArray() - | _ -> throw() - - and parseString() = - ensure(i < s.Length && s.[i] = '"') - i <- i + 1 - while i < s.Length && s.[i] <> '"' do - if s.[i] = '\\' then - ensure(i+1 < s.Length) - match s.[i+1] with - | 'b' -> buf.Append('\b') |> ignore - | 'f' -> buf.Append('\f') |> ignore - | 'n' -> buf.Append('\n') |> ignore - | 't' -> buf.Append('\t') |> ignore - | 'r' -> buf.Append('\r') |> ignore - | '\\' -> buf.Append('\\') |> ignore - | '/' -> buf.Append('/') |> ignore - | '"' -> buf.Append('"') |> ignore - | 'u' -> - ensure(i+5 < s.Length) - let hexdigit d = - if d >= '0' && d <= '9' then int32 d - int32 '0' - elif d >= 'a' && d <= 'f' then int32 d - int32 'a' + 10 - elif d >= 'A' && d <= 'F' then int32 d - int32 'A' + 10 - else failwith "hexdigit" - let unicodeChar (s:string) = - if s.Length <> 4 then failwith "unicodeChar"; - char (hexdigit s.[0] * 4096 + hexdigit s.[1] * 256 + hexdigit s.[2] * 16 + hexdigit s.[3]) - let ch = unicodeChar (s.Substring(i+2, 4)) - buf.Append(ch) |> ignore - i <- i + 4 - | 'U' -> - ensure(i+9 < s.Length) - let unicodeChar (s:string) = - if s.Length <> 8 then failwith "unicodeChar"; - if s.[0..1] <> "00" then failwith "unicodeChar"; - UnicodeHelper.getUnicodeSurrogatePair <| System.UInt32.Parse(s, NumberStyles.HexNumber) - let lead, trail = unicodeChar (s.Substring(i+2, 8)) - buf.Append(lead) |> ignore - buf.Append(trail) |> ignore - i <- i + 8 - | _ -> throw() - i <- i + 2 - else - buf.Append(s.[i]) |> ignore - i <- i + 1 - ensure(i < s.Length && s.[i] = '"') - i <- i + 1 - let str = buf.ToString() - buf.Clear() |> ignore - str - - and parseNum() = - let start = i - while i < s.Length && (isNumChar s.[i]) do - i <- i + 1 - let len = i - start - let sub = s.Substring(start,len) - let asFloat (sub : string) = - match TextConversions.AsFloat [| |] false CultureInfo.InvariantCulture sub with - | Some x -> JsonValue.Float x - | _ -> throw() - if Seq.forall isIntChar sub - then - match TextConversions.AsInteger CultureInfo.InvariantCulture sub with - | Some x -> JsonValue.Integer x - | _ -> asFloat sub - else asFloat sub - - and parsePair() = - let key = parseString() - skipWhitespace() - ensure(i < s.Length && s.[i] = ':') - i <- i + 1 - skipWhitespace() - key, parseValue() - - and parseObject() = - ensure(i < s.Length && s.[i] = '{') - i <- i + 1 - skipWhitespace() - let pairs = ResizeArray<_>() - if i < s.Length && s.[i] = '"' then - pairs.Add(parsePair()) - skipWhitespace() - while i < s.Length && s.[i] = ',' do - i <- i + 1 - skipWhitespace() - pairs.Add(parsePair()) - skipWhitespace() - ensure(i < s.Length && s.[i] = '}') - i <- i + 1 - JsonValue.Record(pairs.ToArray()) - - and parseArray() = - ensure(i < s.Length && s.[i] = '[') - i <- i + 1 - skipWhitespace() - let vals = ResizeArray<_>() - if i < s.Length && s.[i] <> ']' then - vals.Add(parseValue()) - skipWhitespace() - while i < s.Length && s.[i] = ',' do - i <- i + 1 - skipWhitespace() - vals.Add(parseValue()) - skipWhitespace() - ensure(i < s.Length && s.[i] = ']') - i <- i + 1 - JsonValue.Array(vals.ToArray()) - - and parseLiteral(expected, r) = - ensure(i+expected.Length < s.Length) - for j in 0 .. expected.Length - 1 do - ensure(s.[i+j] = expected.[j]) - i <- i + expected.Length - r - - member __.Parse() = - let value = parseRootValue() - skipWhitespace() - if i <> s.Length then - throw() - value - -type JsonValue with - /// Parses the specified JSON string. - static member Parse(text) = JsonParser(text).Parse() - - /// Attempts to parse the specified JSON string. - static member TryParse(text) = - try Some <| JsonParser(text).Parse() - with _ -> None \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Client/ReflectionPatterns.fs b/src/FSharp.Data.GraphQL.Client/ReflectionPatterns.fs index 30fc94248..443cdb6a3 100644 --- a/src/FSharp.Data.GraphQL.Client/ReflectionPatterns.fs +++ b/src/FSharp.Data.GraphQL.Client/ReflectionPatterns.fs @@ -4,39 +4,13 @@ namespace FSharp.Data.GraphQL.Client open System -open System.Collections open Microsoft.FSharp.Reflection module ReflectionPatterns = - let private numericTypes = - [| typeof - typeof - typeof - typeof - typeof - typeof - typeof - typeof - typeof - typeof - typeof |] let isOption (t : Type) = t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<_ option> - let isMap (t : Type) = - t = typeof> - - let isList (t : Type) = - if t.IsGenericType - then t.GetGenericTypeDefinition() = typedefof<_ list> - else false - - let isSeq (t : Type) = - if t.IsGenericType - then t.GetGenericTypeDefinition() = typedefof> - else false - let getOptionCases (t: Type) = let otype = typedefof<_ option>.MakeGenericType(t) let cases = FSharpType.GetUnionCases(otype) @@ -74,54 +48,6 @@ module ReflectionPatterns = if isOption t then Some (Option (t.GetGenericArguments().[0])) else None - let isType (expected : Type) (t : Type) = - match t with - | Option t -> t = expected - | _ -> t = expected - - let isNumericType (t : Type) = - numericTypes |> Array.exists (fun expected -> isType expected t) - let (|Array|_|) (t : Type) = if t.IsArray then Some (Array (t.GetElementType())) - else None - - let (|List|_|) (t : Type) = - if isList t then Some (List (t.GetGenericArguments().[0])) - else None - - let (|Seq|_|) (t : Type) = - if isSeq t then Some (Seq (t.GetGenericArguments().[0])) - else None - - let (|EnumerableValue|_|) (x : obj) = - match x with - | :? IEnumerable as x -> Some (EnumerableValue (Seq.cast x |> Array.ofSeq)) - | _ -> None - - let (|OptionValue|_|) (x : obj) = - let xtype = x.GetType() - if isOption xtype - then - match FSharpValue.GetUnionFields(x, xtype) with - | (_, [|value|]) -> Some (OptionValue Some value) - | _ -> Some (OptionValue None) - else None - - let (|EnumValue|_|) (x : obj) = - let xtype = x.GetType() - if xtype.IsEnum - then Some (x.ToString()) - else None - - let makeValue (t : Type) (value : obj) = - let isOption = isOption t - match value, isOption with - | null, true -> makeNone t - | OptionValue (Some null), true -> box (makeSome (Convert.ChangeType(null, t))) - | OptionValue (Some value), true -> box (makeSome value) - | OptionValue (Some value), false -> Convert.ChangeType(value, t) - | OptionValue None, false -> Convert.ChangeType(null, t) - | OptionValue None, true -> box (makeNone t) - | value, true -> makeSome value - | value, false -> Convert.ChangeType(value, t) + else None \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Client/Schema.fs b/src/FSharp.Data.GraphQL.Client/Schema.fs index 3b054b360..f742a8fcf 100644 --- a/src/FSharp.Data.GraphQL.Client/Schema.fs +++ b/src/FSharp.Data.GraphQL.Client/Schema.fs @@ -3,6 +3,8 @@ namespace FSharp.Data.GraphQL.Client +open System +open FSharp.Data.GraphQL open FSharp.Data.GraphQL.Types.Introspection type internal GraphQLResponse<'T> = { @@ -12,4 +14,103 @@ type internal GraphQLResponse<'T> = { type internal IntrospectionResult = { __schema: IntrospectionSchema -} \ No newline at end of file +} + + +module internal TypeMapping = + let scalar = + [| "Int", typeof + "Boolean", typeof + "Date", typeof + "Float", typeof + "ID", typeof + "String", typeof + "URI", typeof + "Guid", typeof |] + |> Map.ofArray + + let getSchemaTypes (introspection : IntrospectionSchema) = + let schemaTypeNames = + [| "__TypeKind" + "__DirectiveLocation" + "__Type" + "__InputValue" + "__Field" + "__EnumValue" + "__Directive" + "__Schema" |] + + let isScalarType (name : string) = + scalar |> Map.containsKey name + let isIntrospectionType (name : string) = + schemaTypeNames |> Array.contains name + introspection.Types + |> Array.choose (fun t -> + if not (isIntrospectionType t.Name) && not (isScalarType t.Name) + then Some(t.Name, t) + else None) + |> Map.ofArray + + let mapScalarType uploadInputTypeName tname = + match uploadInputTypeName with + | Some uploadInputTypeName when uploadInputTypeName = tname -> typeof + | _ -> + // Unknown scalar types will be mapped to a string type. + if scalar.ContainsKey(tname) + then scalar.[tname] + else typeof + + let makeOption (t : Type) = typedefof<_ option>.MakeGenericType(t) + + let makeArray (t : Type) = t.MakeArrayType() + + let unwrapOption (t : Type) = + if t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<_ option> + then t.GetGenericArguments().[0] + else failwithf "Expected type to be an Option type, but it is %s." t.Name + + let makeAsync (t : Type) = typedefof>.MakeGenericType(t) + + + +type SchemaTypes (schema: IntrospectionSchema, ?uploadInputTypeName: string) = + let schemaTypes = TypeMapping.getSchemaTypes schema + let uploadInputType = + uploadInputTypeName + |> Option.map (fun name -> Map.tryFind name schemaTypes) + |> Option.flatten + do match uploadInputType with + | Some(schemaType) when schemaType.Kind = TypeKind.SCALAR -> () + | None _ -> () + | Some schemaType -> failwithf "Upload type '%s' must be a scalar." schemaType.Name + + member _.UploadInputType = uploadInputType + + member _.Types = schemaTypes + + member _.Introspection = schema + + member _.ContainsType (name: string) = + schemaTypes.ContainsKey(name) + + member _.TryFindType(name: string) = + schemaTypes.TryFind(name) + + member _.FindType(name: string) = + match schemaTypes.TryFind(name) with + | Some itype -> itype + | None -> failwithf "Type \"%s\" was not found on the schema custom types." name + + member this.FindByTypeRef(tref: IntrospectionTypeRef) = + match tref.Name with + | Some name -> this.FindType name + | None -> failwith "Expected schema type to have a name, but it does not have one." + + +[] +module internal Patterns = + let (|NamedTypeRef|ContainerTypeRef|) (x: IntrospectionTypeRef) = + match x.Name, x.OfType with + | Some name, None -> NamedTypeRef(x.Kind, name) + | None, Some innerType -> ContainerTypeRef(x.Kind, innerType) + | _, _ -> failwithf "Invalid introspection type ref. Expected named or container type %A" x diff --git a/src/FSharp.Data.GraphQL.Client/Serialization.fs b/src/FSharp.Data.GraphQL.Client/Serialization.fs index 81eb78ba1..84277ceac 100644 --- a/src/FSharp.Data.GraphQL.Client/Serialization.fs +++ b/src/FSharp.Data.GraphQL.Client/Serialization.fs @@ -4,198 +4,632 @@ namespace FSharp.Data.GraphQL.Client open System -open Microsoft.FSharp.Reflection +open System.IO +open System.Text.Json +open System.Text.Json.Serialization open System.Reflection +open System.Collections.Concurrent open System.Collections.Generic -open System.Globalization open FSharp.Data.GraphQL -open FSharp.Data.GraphQL.Client.ReflectionPatterns +open FSharp.Data.GraphQL.Types.Introspection +open FSharp.Data.GraphQL.Ast +open FSharp.Data.GraphQL.Ast.Extensions +open FSharp.Reflection +open FSharp.Quotations -module Serialization = - let private makeOption t (value : obj) = - let otype = typedefof<_ option> - let cases = FSharpType.GetUnionCases(otype.MakeGenericType([|t|])) + +type OptionConverter<'T> () = + inherit JsonConverter>() + override _.Read(reader, typeToConvert, options) = + match reader.TokenType with + | JsonTokenType.Null -> None + | _ -> Some <| JsonSerializer.Deserialize<'T>(&reader, options) + + override _.Write(writer, value, options) = match value with - | null -> FSharpValue.MakeUnion(cases.[0], [||]) - | _ -> FSharpValue.MakeUnion(cases.[1], [|value|]) - - let private downcastNone<'T> t = - match t with - | Option t -> downcast (makeOption t null) - | _ -> failwithf "Error parsing JSON value: %O is not an option value." t - - let private downcastType (t : Type) x = - match t with - | Option t -> downcast (makeOption t (Convert.ChangeType(x, t))) - | _ -> downcast (Convert.ChangeType(x, t)) - - let private isStringType = isType typeof - let private isDateTimeType = isType typeof - let private isDateTimeOffsetType = isType typeof - let private isUriType = isType typeof - let private isGuidType = isType typeof - let private isBooleanType = isType typeof - let private isEnumType = function (Option t | t) when t.IsEnum -> true | _ -> false - - let private downcastString (t : Type) (s : string) = - match t with - | t when isStringType t -> downcastType t s - | t when isUriType t -> - match Uri.TryCreate(s, UriKind.RelativeOrAbsolute) with - | (true, uri) -> downcastType t uri - | _ -> failwithf "Error parsing JSON value: %O is an URI type, but parsing of value \"%s\" failed." t s - | t when isDateTimeType t -> - match DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None) with - | (true, d) -> downcastType t d - | _ -> failwithf "Error parsing JSON value: %O is a date type, but parsing of value \"%s\" failed." t s - | t when isDateTimeOffsetType t -> - match DateTimeOffset.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.None) with - | (true, d) -> downcastType t d - | _ -> failwithf "Error parsing JSON value: %O is a date time offset type, but parsing of value \"%s\" failed." t s - | t when isGuidType t -> - match Guid.TryParse(s) with - | (true, g) -> downcastType t g - | _ -> failwithf "Error parsing JSON value: %O is a Guid type, but parsing of value \"%s\" failed." t s - | t when isEnumType t -> - match t with - | (Option et | et) -> - try Enum.Parse(et, s) |> downcastType t - with _ -> failwithf "Error parsing JSON value: %O is a Enum type, but parsing of value \"%s\" failed." t s - | _ -> failwithf "Error parsing JSON value: %O is not a enum type." t - | _ -> failwithf "Error parsing JSON value: %O is not a string type." t - - let private downcastBoolean (t : Type) b = - match t with - | t when isBooleanType t -> downcastType t b - | _ -> failwithf "Error parsing JSON value: %O is not a boolean type." t - - let rec private getArrayValue (t : Type) (converter : Type -> JsonValue -> obj) (items : JsonValue []) = - let castArray itemType (items : obj []) : obj = - let arr = Array.CreateInstance(itemType, items.Length) - items |> Array.iteri (fun ix i -> arr.SetValue(i, ix)) - upcast arr - let castList itemType (items : obj list) = - let tlist = typedefof<_ list>.MakeGenericType([|itemType|]) - let empty = - let uc = - Reflection.FSharpType.GetUnionCases(tlist) - |> Seq.filter (fun uc -> uc.Name = "Empty") - |> Seq.exactlyOne - Reflection.FSharpValue.MakeUnion(uc, [||]) - let rec helper items = - match items with - | [] -> empty - | [x] -> Activator.CreateInstance(tlist, [|x; empty|]) - | x :: xs -> Activator.CreateInstance(tlist, [|x; helper xs|]) - helper items - Tracer.runAndMeasureExecutionTime "Converted Array JsonValue to CLR array" (fun _ -> - match t with - | Option t -> getArrayValue t converter items |> makeOption t - | Array itype | Seq itype -> items |> Array.map (converter itype) |> castArray itype - | List itype -> items |> Array.map (converter itype) |> Array.toList |> castList itype - | _ -> failwithf "Error parsing JSON value: %O is not an array type." t) - - let private downcastNumber (t : Type) n = - match t with - | t when isNumericType t -> downcastType t n - | _ -> failwithf "Error parsing JSON value: %O is not a numeric type." t - - let rec private convert t parsed : obj = - Tracer.runAndMeasureExecutionTime (sprintf "Converted JsonValue to %O type." t) (fun _ -> - match parsed with - | JsonValue.Null -> downcastNone t - | JsonValue.String s -> downcastString t s - | JsonValue.Float n -> downcastNumber t n - | JsonValue.Integer n -> downcastNumber t n - | JsonValue.Record jprops -> - let jprops = - jprops - |> Array.map (fun (n, v) -> n.ToLowerInvariant(), v) - |> Map.ofSeq - let tprops t = - FSharpType.GetRecordFields(t, true) - |> Array.map (fun p -> p.Name.ToLowerInvariant(), p.PropertyType) - let vals t = - tprops t - |> Array.map (fun (n, t) -> - match Map.tryFind n jprops with - | Some p -> n, convert t p - | None -> n, makeOption t null) - let rcrd = - let t = match t with Option t -> t | _ -> t - let vals = vals t - if isMap t - then Map.ofArray vals |> box - else FSharpValue.MakeRecord(t, Array.map snd vals, true) - downcastType t rcrd - | JsonValue.Array items -> items |> getArrayValue t convert - | JsonValue.Boolean b -> downcastBoolean t b) - - let deserializeRecord<'T> (json : string) : 'T = - let t = typeof<'T> - Tracer.runAndMeasureExecutionTime (sprintf "Deserialized JSON string to record type %s." (t.ToString())) (fun _ -> - downcast (JsonValue.Parse(json) |> convert t)) - - let deserializeMap values = - let rec helper (values : (string * JsonValue) []) = - values - |> Array.map (fun (name, value) -> - match value with - | JsonValue.Record fields -> name, (fields |> helper |> Map.ofArray |> box) - | JsonValue.Null -> name, null - | JsonValue.String s -> name, box s - | JsonValue.Integer n -> name, box n - | JsonValue.Float f -> name, box f - | JsonValue.Array items -> name, (items |> Array.map (fun item -> null, item) |> helper |> Array.map snd |> box) - | JsonValue.Boolean b -> name, box b) - Tracer.runAndMeasureExecutionTime "Deserialized JSON Record into FSharp Map" (fun _ -> - helper values |> Map.ofArray) - - let private isoDateFormat = "yyyy-MM-dd" - let private isoDateTimeFormat = "O" - - let rec toJsonValue (x : obj) : JsonValue = - if isNull x - then JsonValue.Null + | None -> writer.WriteNullValue() + | Some x -> JsonSerializer.Serialize<'T>(writer, x, options) + +type OptionConverterFactory () = + inherit JsonConverterFactory() + let optionTypeConverter = typedefof> + override _.CanConvert(typeToConvert) = + typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() = typedefof> + + override _.CreateConverter(typeToConvert, options) = + optionTypeConverter + .MakeGenericType(typeToConvert.GetGenericArguments()) + .GetConstructor([||]) + .Invoke([||]) :?> JsonConverter + + +type RecordConverter<'T> () = + inherit JsonConverter<'T>() + let isNullableField (propertyInfo: PropertyInfo) = + let propType = propertyInfo.PropertyType + propType.IsGenericType && + (let genericType = propType.GetGenericTypeDefinition() + genericType = typedefof> || genericType = typedefof>) + + let makeRecord = FSharpValue.PreComputeRecordConstructor(typeof<'T>, true) + let readRecord = FSharpValue.PreComputeRecordReader(typeof<'T>, true) + + let recordFields = + let flags = BindingFlags.Public ||| BindingFlags.NonPublic + [| for field in FSharpType.GetRecordFields(typeof<'T>, flags) do + (field.Name, isNullableField field, field.PropertyType) |] + + let fieldDefaults = + [| for (_,_,fieldType) in recordFields do + if fieldType.IsValueType + then Activator.CreateInstance(fieldType) + else null |] + + let tryGetFieldPosition = + let cache = ConcurrentDictionary() + fun (caseInsensitive: bool) (propertyName: string) -> + let key = (caseInsensitive, propertyName) + match cache.TryGetValue key with + | true, v -> + v + | false, _ -> + let stringCompare = if caseInsensitive then StringComparison.InvariantCultureIgnoreCase else StringComparison.InvariantCulture + let index = recordFields |> Array.tryFindIndex(fun (v, _, _) -> v.Equals(propertyName, stringCompare)) + cache.[key] <- index + index + + override _.Read(reader, typeToConvert, options) = + if reader.TokenType <> JsonTokenType.StartObject then + failwith "Expected Json Start Object Token" + let mutable cont = true + let values = Array.copy fieldDefaults + let tryGetPosition = tryGetFieldPosition options.PropertyNameCaseInsensitive + while cont do + match reader.Read(), reader.TokenType with + | true, JsonTokenType.EndObject -> + cont <- false + | true, JsonTokenType.PropertyName -> + let propertyName = reader.GetString() + match tryGetPosition propertyName with + | Some pos -> + let (fieldName, isNullable, propertyType) = recordFields.[pos] + let value = JsonSerializer.Deserialize(&reader, propertyType, options) + if (not isNullable) && isNull value then + failwithf "Field '%s' is required" fieldName + values.[pos] <- value + | None -> + reader.Skip() + () + | false, _ -> + failwith "Unexpected End of JSON Sequence" + | _, token -> + failwithf "Unexpected token '%A' in JSON Sequence" token + let record = makeRecord values + record :?> 'T + + + override _.Write(writer, value, options) = + writer.WriteStartObject() + let values = readRecord value + let normalizeName = + if isNull options.PropertyNamingPolicy + then id + else options.PropertyNamingPolicy.ConvertName + for i = 0 to values.Length - 1 do + let (name, _, propertyType) = recordFields.[i] + writer.WritePropertyName(normalizeName name) + JsonSerializer.Serialize(writer, values.[i], propertyType, options) + writer.WriteEndObject() + +type RecordConverterFactory () = + inherit JsonConverterFactory() + let recordTypeConverter = typedefof> + + override _.CanConvert(typeToConvert) = + FSharpType.IsRecord(typeToConvert, true) + + override _.CreateConverter(typeToConvert, options) = + recordTypeConverter + .MakeGenericType(typeToConvert) + .GetConstructor([||]) + .Invoke([||]) :?> JsonConverter + +// Converted from FSharp.SystemTextJson +type MapConverter<'V> () = + inherit JsonConverter>() + let rec read (acc: Map) (reader: byref) options = + if not (reader.Read()) then acc else + match reader.TokenType with + | JsonTokenType.EndObject -> acc + | JsonTokenType.PropertyName -> + let key = reader.GetString() + let value = + // if the value type is obj, we want to default to + // the map converter whenever we see an object + if typeof<'V> = typeof + && (let peekReader = reader + peekReader.Read() |> ignore + peekReader.TokenType = JsonTokenType.StartObject) + then JsonSerializer.Deserialize>(&reader, options) :> obj :?> 'V + else JsonSerializer.Deserialize<'V>(&reader, options) + read (Map.add key value acc) &reader options + | _ -> + failwithf "Failed to serialize map" + + override _.Read(reader, _typeToConvert, options) = + read Map.empty &reader options + + override _.Write(writer, value, options) = + writer.WriteStartObject() + for kv in value do + let k = + match options.DictionaryKeyPolicy with + | null -> kv.Key + | p -> p.ConvertName kv.Key + writer.WritePropertyName(k) + JsonSerializer.Serialize<'V>(writer, kv.Value, options) + writer.WriteEndObject() + +type MapConverterFactory () = + inherit JsonConverterFactory() + let mapTypeConverter = typedefof> + override _.CanConvert(typeToConvert) = + if typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() = typedefof> then + let types = typeToConvert.GetGenericArguments() + types.[0] = typeof else - let t = x.GetType() - Tracer.runAndMeasureExecutionTime (sprintf "Converted object type %s to JsonValue" (t.ToString())) (fun _ -> - match x with - | null -> JsonValue.Null - | OptionValue None -> JsonValue.Null - | :? int as x -> JsonValue.Integer (int x) - | :? float as x -> JsonValue.Float x - | :? string as x -> JsonValue.String x - | :? Guid as x -> JsonValue.String (x.ToString()) - | :? DateTime as x when x.Date = x -> JsonValue.String (x.ToString(isoDateFormat)) - | :? DateTime as x -> JsonValue.String (x.ToString(isoDateTimeFormat)) - | :? DateTimeOffset as x -> JsonValue.String (x.ToString(isoDateTimeFormat)) - | :? bool as x -> JsonValue.Boolean x - | :? Uri as x -> JsonValue.String (x.ToString()) - | :? Upload -> JsonValue.Null - | :? IDictionary as items -> - items - |> Seq.map (fun (KeyValue (k, v)) -> k.FirstCharLower(), toJsonValue v) - |> Seq.toArray - |> JsonValue.Record - | EnumerableValue items -> - items - |> Array.map toJsonValue - |> JsonValue.Array - | OptionValue (Some x) -> toJsonValue x - | EnumValue x -> JsonValue.String x + false + override _.CreateConverter(typeToConvert, options) = + let valueType = typeToConvert.GetGenericArguments().[1] + mapTypeConverter + .MakeGenericType([|valueType|]) + .GetConstructor([||]) + .Invoke([||]) :?> JsonConverter + +type UploadConverter () = + inherit JsonConverter() + + override _.Read(_, _, _) = + invalidOp "The type 'Upload' is write-only" + + override _.Write(writer, _, _) = + writer.WriteNullValue() + +type RecordBaseConverter () = + inherit JsonConverter() + + override _.Read(reader, _typeToConvert, options) = + invalidOp "The type 'RecordBase' is write-only" + + override _.Write(writer, value, options) = + let properties = value.ToDictionary() + JsonSerializer.Serialize(writer, properties, options) + +type EnumBaseConverter () = + inherit JsonConverter() + + override _.Read(reader, _typeToConvert, options) = + invalidOp "The type 'EnumBase' is write-only" + + override _.Write(writer, value, options) = + JsonSerializer.Serialize(writer, value.GetValue(), options) + + +[] +module RuntimeSerialization = + + let options = JsonSerializerOptions(PropertyNameCaseInsensitive=true, PropertyNamingPolicy=JsonNamingPolicy.CamelCase) + options.Converters.Add(OptionConverterFactory()) + options.Converters.Add(RecordConverterFactory()) + options.Converters.Add(JsonStringEnumConverter()) + options.Converters.Add(MapConverterFactory()) + options.Converters.Add(UploadConverter()) + options.Converters.Add(RecordBaseConverter()) + options.Converters.Add(EnumBaseConverter()) + + let getTypeName (element: JsonElement) = + match element.TryGetProperty("__typename") with + | true, value -> value.GetString() + | false, _ -> failwith "__typename not found in json" + + let tryReadString (element: JsonElement) = + match element.GetString() with + | null -> None + | s -> Some s + + let tryReadBool (element: JsonElement) = + match element.ValueKind with + | JsonValueKind.True -> Some true + | JsonValueKind.False -> Some false + | _ -> None + + let tryReadInt (element: JsonElement) = + match element.TryGetInt32() with + | true, value -> Some value + | _ -> None + + let tryReadFloat (element: JsonElement) = + match element.TryGetDouble() with + | true, value -> Some value + | _ -> None + + let tryReadDateTime (element: JsonElement) = + match element.TryGetDateTime() with + | true, value -> Some value + | _ -> None + + let tryReadUri (element: JsonElement) = + match Uri.TryCreate(element.GetString(), UriKind.RelativeOrAbsolute) with + | true, value -> Some value + | false, _ -> None + + let tryReadGuid (element: JsonElement) = + match element.TryGetGuid() with + | true, value -> Some value + | false, _ -> None + + let tryReadProperty (name: string) (element: JsonElement) = + match element.TryGetProperty(name) with + | true, value -> ValueSome value + | false, _ -> ValueNone + + let readProperty (name: string) (element: JsonElement) = + match element.TryGetProperty(name) with + | true, value -> value + | false, _ -> failwithf "'%s' is required, but was not found in result" name + + let readString (element: JsonElement) = + element.GetString() + + let readBool (element: JsonElement) = + element.GetBoolean() + + let readInt (element: JsonElement) = + element.GetInt32() + + let readFloat (element: JsonElement) = + element.GetDouble() + + let readDateTime (element: JsonElement) = + element.GetDateTime() + + let readUri (element: JsonElement) = + Uri(element.GetString()) + + let readGuid (element: JsonElement) = + element.GetGuid() + + let getPossibleTypes (schemaTypes: SchemaTypes) (abstractType: IntrospectionType) (abstractTypeName: string) (selection: AstFieldInfo) = + match abstractType.PossibleTypes with + | Some possibleTypes -> + let fieldsByType = + selection.Fields + |> List.groupBy (function | TypeField _ -> abstractTypeName | FragmentField field -> field.TypeCondition) + |> Map.ofList + let getFieldsByTypeName typeName = + fieldsByType + |> Map.tryFind typeName + |> Option.defaultValue [] + let commonFields = getFieldsByTypeName abstractTypeName + [ for possibleType in possibleTypes do + let objectType = schemaTypes.FindByTypeRef(possibleType) + let possibleFields = commonFields @ getFieldsByTypeName objectType.Name + objectType, possibleFields ] + | None -> + [ abstractType, selection.Fields ] + + let readErrorPath (element: JsonElement) = + match tryReadProperty "path" element with + | ValueSome pathElement when pathElement.ValueKind = JsonValueKind.Array -> + use pathEnumerator = pathElement.EnumerateArray() + [| for element in pathEnumerator do + if element.ValueKind = JsonValueKind.String + then element.GetString() |> box + else element.GetInt32() |> box |] + | _ -> + [||] + + let readErrors (element: JsonElement) = + use enumerator = element.EnumerateArray() + [| for element in enumerator do + let message = readString (readProperty "message" element) + let path = readErrorPath element + (message, path) |] + + let readCustomData (element: JsonElement) = + use enumerator = element.EnumerateObject() + let customData = + [| for element in enumerator do + match element.Name with + | "data" | "errors" -> + () + | name -> + let serializationType = + match element.Value.ValueKind with + | JsonValueKind.Object -> typeof> + | _ -> typeof + name, JsonSerializer.Deserialize(element.Value.GetRawText(), serializationType, options) |] + Map.ofArray customData + +module DesignTimeSerialization = + + type IExprVisitor<'R> = + abstract Visit<'T> : unit -> 'R + + type IExprActivator = + abstract Accept : v: IExprVisitor<'R> -> 'R + + type ExprActivator<'T> () = + interface IExprActivator with + member _.Accept(v: IExprVisitor<'R>) = + v.Visit<'T>() + + type Expr with + member e.Accept (v: IExprVisitor<'R>) = + let ty = typedefof>.MakeGenericType e.Type + let inst = Activator.CreateInstance(ty) :?> IExprActivator + inst.Accept(v) + + member e.AcceptLambda (v: IExprVisitor<'R>) = + if FSharpType.IsFunction e.Type then + let (_, c) = FSharpType.GetFunctionElements e.Type + let ty = typedefof>.MakeGenericType c + let inst = Activator.CreateInstance(ty) :?> IExprActivator + inst.Accept(v) + else + invalidOp "expected Expr 'c>" + + + let inline reader (f: (JsonElement -> 'a)) (elementExpr: Expr) = + <@ f %elementExpr @> :> Expr + + let inline optionalReader (f: (JsonElement -> 'a option)) (elementExpr: Expr) = + <@ match (%elementExpr).ValueKind with + | JsonValueKind.Null | JsonValueKind.Undefined -> None + | _ -> f (%elementExpr) @> :> Expr + + let makeArrayExpr (elementExpr: Expr) (body: Expr 'T>) (isNullable: bool) = + if isNullable then + <@@ let element: JsonElement = %elementExpr + match element.ValueKind with + | JsonValueKind.Array -> + use enumerator = element.EnumerateArray() + Some [| for element in enumerator -> (%body) element |] | _ -> - let props = t.GetProperties(BindingFlags.Public ||| BindingFlags.Instance) - let items = props |> Array.map (fun p -> (p.Name.FirstCharLower(), p.GetValue(x) |> toJsonValue)) - JsonValue.Record items) - - let serializeRecord (x : obj) = - Tracer.runAndMeasureExecutionTime (sprintf "Serialized object type %s to a JSON string" (x.GetType().ToString())) (fun _ -> - (toJsonValue x).ToString()) - - let deserializeSchema (json : string) = - Tracer.runAndMeasureExecutionTime "Deserialized schema" (fun _ -> - let result = deserializeRecord> json - match result.Errors with - | None -> result.Data.__schema - | Some errors -> String.concat "\n" errors |> failwithf "%s") \ No newline at end of file + None + @@> + else + <@@ let element: JsonElement = %elementExpr + match element.ValueKind with + | JsonValueKind.Array -> + use enumerator = element.EnumerateArray() + [| for element in enumerator -> (%body) element |] + | valueKind -> + failwithf "Expected array type but received type: %A" valueKind + @@> + + + let rec tryReadObject (schemaTypes: SchemaTypes) (introspectionType: IntrospectionType) (selections: list) (elementExpr: Expr) = + let fieldExprs = makeFieldExpressions schemaTypes introspectionType selections elementExpr + <@@ match (%elementExpr).ValueKind with + | JsonValueKind.Object -> + let properties = Dictionary() + let typeName = getTypeName %elementExpr + (%%fieldExprs) properties + Some(RecordBase(typeName, properties)) + | _ -> + None @@> + and readObject (schemaTypes: SchemaTypes) (introspectionType: IntrospectionType) (selections: list) (elementExpr: Expr) = + let fieldExprs = makeFieldExpressions schemaTypes introspectionType selections elementExpr + <@@ let element = %elementExpr + let properties = Dictionary() + (%%fieldExprs) properties + let typeName = getTypeName element + RecordBase(typeName, properties) @@> + and makeFieldExpressions (schemaTypes: SchemaTypes) (introspectionType: IntrospectionType) (selections: list) (elementExpr: Expr) = + let fields = Option.defaultValue [||] introspectionType.Fields + let fieldsMap = + fields + |> Array.map(fun field -> field.Name, field) + |> Map.ofArray + let propertiesVar = Var("properties", typeof>) + let propertiesExpr = Expr.Var(propertiesVar) + let makeSelectionExpr (selection: AstFieldInfo) = + let propertyName = selection.AliasOrName + let readerExpr: Expr = + let selectionField = fieldsMap.[selection.Name] + let isNullable = match selectionField.Type with ContainerTypeRef(TypeKind.NON_NULL, _) -> false | _ -> true + let elementExpr = + if isNullable then + <@ match tryReadProperty propertyName %elementExpr with + | ValueSome element -> element + | ValueNone -> Unchecked.defaultof @> + else + <@ readProperty propertyName %elementExpr @> + makeElementReader schemaTypes selectionField selection selectionField.Type true elementExpr + readerExpr.Accept + { new IExprVisitor with + member _.Visit<'r> () = + <@@ let properties:Dictionary = %%propertiesExpr + properties.[propertyName] <- (%%readerExpr : 'r) @@> + } + let fieldExpressions = + selections + |> List.filter(fun s -> s.Name <> "__typename") + |> List.map makeSelectionExpr + |> List.reduce(fun e1 e2 -> Expr.Sequential(e1, e2)) + Expr.Lambda(propertiesVar, fieldExpressions) + and makeElementReader (schemaTypes: SchemaTypes) (field: IntrospectionField) (selection: AstFieldInfo) (fieldType: IntrospectionTypeRef) (isNullable: bool) = + match fieldType with + | ContainerTypeRef(TypeKind.NON_NULL, innerTypeRef) -> + makeElementReader schemaTypes field selection innerTypeRef false + | ContainerTypeRef(TypeKind.LIST, innerTypeRef) -> + let body = + let elementVar = Var("element", typeof) + let elementExpr = Expr.Var(elementVar) |> Expr.Cast + let elementReader = makeElementReader schemaTypes field selection innerTypeRef true + Expr.Lambda(elementVar, elementReader elementExpr) + fun elementExpr -> + body.AcceptLambda + { new IExprVisitor with + member _.Visit<'r> () = + let expr = Expr.Cast 'r> body + makeArrayExpr elementExpr expr isNullable } + | NamedTypeRef(TypeKind.OBJECT, name) -> + let objectType = schemaTypes.FindType(name) + if isNullable + then tryReadObject schemaTypes objectType selection.Fields + else readObject schemaTypes objectType selection.Fields + | NamedTypeRef((TypeKind.UNION | TypeKind.INTERFACE), abstractTypeName) -> + let abstractType = schemaTypes.FindType(abstractTypeName) + let implementations = getPossibleTypes schemaTypes abstractType abstractTypeName selection + let typeNameVar = Var("typeVar", typeof) + let typeNameExpr = Expr.Var(typeNameVar) + fun elementExpr -> + let objectReader = if isNullable then tryReadObject else readObject + let readConcreteType = + let rec makeConditionalRecord (elements: list>) = + match elements with + | (objectType, fields)::tail -> + let typeName = objectType.Name + let readExpr = objectReader schemaTypes objectType fields elementExpr + Expr.IfThenElse(<@@ %%typeNameExpr = typeName @@>, readExpr, makeConditionalRecord tail) + | [] when isNullable -> <@@ None : RecordBase option @@> + | [] -> <@@ failwithf "Unknown descriminator for type %s" abstractTypeName @@> + makeConditionalRecord implementations + Expr.Let(typeNameVar, <@@ getTypeName (%elementExpr) @@>, readConcreteType) + | NamedTypeRef(TypeKind.ENUM, typeName) -> + fun expr -> + if isNullable then + <@@ match tryReadString %expr with + | Some value -> + Some(EnumBase(typeName, value)) + | None -> + None @@> + else + <@@ EnumBase(typeName, readString %expr) @@> + | NamedTypeRef(TypeKind.SCALAR, typeName) -> + match typeName with + | "String" | "ID" -> + if isNullable then + fun elementExpr -> + <@ match (%elementExpr).ValueKind with + | JsonValueKind.Null | JsonValueKind.Undefined -> None + | _ -> tryReadString (%elementExpr) @> :> Expr + else + fun elementExpr -> + <@ readString (%elementExpr) @> :> Expr + | "Int" -> + if isNullable then + fun elementExpr -> + <@ match (%elementExpr).ValueKind with + | JsonValueKind.Null | JsonValueKind.Undefined -> None + | _ -> tryReadInt (%elementExpr) @> :> Expr + else + fun elementExpr -> + <@ readInt (%elementExpr) @> :> Expr + | "Boolean" -> + if isNullable then + fun elementExpr -> + <@ match (%elementExpr).ValueKind with + | JsonValueKind.Null | JsonValueKind.Undefined -> None + | _ -> tryReadBool (%elementExpr) @> :> Expr + else + fun elementExpr -> + <@ readBool (%elementExpr) @> :> Expr + | "Float" -> + if isNullable then + fun elementExpr -> + <@ match (%elementExpr).ValueKind with + | JsonValueKind.Null | JsonValueKind.Undefined -> None + | _ -> tryReadFloat (%elementExpr) @> :> Expr + else + fun elementExpr -> + <@ readFloat (%elementExpr) @> :> Expr + | "Date" -> + if isNullable then + fun elementExpr -> + <@ match (%elementExpr).ValueKind with + | JsonValueKind.Null | JsonValueKind.Undefined -> None + | _ -> tryReadDateTime (%elementExpr) @> :> Expr + else + fun elementExpr -> + <@ readDateTime (%elementExpr) @> :> Expr + | "Guid" -> + if isNullable then + fun elementExpr -> + <@ match (%elementExpr).ValueKind with + | JsonValueKind.Null | JsonValueKind.Undefined -> None + | _ -> tryReadGuid (%elementExpr) @> :> Expr + else + fun elementExpr -> + <@ readGuid (%elementExpr) @> :> Expr + | "URI" -> + if isNullable then + fun elementExpr -> + <@ match (%elementExpr).ValueKind with + | JsonValueKind.Null | JsonValueKind.Undefined -> None + | _ -> tryReadUri (%elementExpr) @> :> Expr + else + fun elementExpr -> + <@ readUri (%elementExpr) @> :> Expr + | scalar -> failwithf "%s is not supported" scalar + | other -> + failwithf "%A not supported" other + + + + let getOperationJsonReader (schemaTypes: SchemaTypes) (selections: list) (operation: OperationDefinition) = + let introspection = schemaTypes.Introspection + let operationTypeRef = + let operationTypeRefOpt = + match operation.OperationType with + | Query -> Some introspection.QueryType + | Mutation -> introspection.MutationType + | Subscription -> introspection.SubscriptionType + match operationTypeRefOpt with + | Some op -> op + | None -> invalidOp (sprintf "top-level %A type is not defined in the schema" operation.OperationType) + let operationType = schemaTypes.FindByTypeRef(operationTypeRef) + let elementVar = Var("element", typeof) + let elementExpr = Expr.Var(elementVar) |> Expr.Cast + let dataElementExpr = + <@ match tryReadProperty "data" %elementExpr with + | ValueSome element -> element + | ValueNone -> Unchecked.defaultof @> + let resultExpr = tryReadObject schemaTypes operationType selections dataElementExpr + let bodyExpr = + <@@ + let element = %elementExpr + let result: RecordBase option = %%resultExpr + let errors: OperationError [] = + match tryReadProperty "errors" element with + | ValueSome errorElement when errorElement.ValueKind = JsonValueKind.Array -> + element + |> readErrors + |> Array.map(fun (m, p) -> { Message = m; Path = p }) + | _ -> + [||] + let customData = readCustomData element + { CustomData = customData; Data = result; Errors = errors } + @@> + Expr.Lambda(elementVar, bodyExpr) + + +module Serialization = + + let deserializeStream<'T> (stream: Stream) = + let vtask = JsonSerializer.DeserializeAsync<'T>(stream, RuntimeSerialization.options) + vtask.AsTask() |> Async.AwaitTask + + let deserializeSchema (stream: Stream) = + let introspectionSchema = + stream + |> deserializeStream> + |> Async.RunSynchronously + introspectionSchema.Data.__schema + + let serializeToStream (value: 'T) (stream: Stream) = async { + do! JsonSerializer.SerializeAsync(stream, value, RuntimeSerialization.options) |> Async.AwaitTask + stream.Position <- 0L + } \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Client/TextConversions.fs b/src/FSharp.Data.GraphQL.Client/TextConversions.fs deleted file mode 100644 index d20f8291f..000000000 --- a/src/FSharp.Data.GraphQL.Client/TextConversions.fs +++ /dev/null @@ -1,122 +0,0 @@ -// -------------------------------------------------------------------------------------- -// Copyright (c) Microsoft Corporation 2005-2012. -// This sample code is provided "as is" without warranty of any kind. -// We disclaim all warranties, either express or implied, including the -// warranties of merchantability and fitness for a particular purpose. -// A simple F# portable parser for JSON data -// -------------------------------------------------------------------------------------- - -namespace FSharp.Data.GraphQL.Client - -open System -open System.Globalization -open System.Text.RegularExpressions - -[] -module private TextConversionHelpers = - let asOption = function true, v -> Some v | _ -> None - - let (|StringEqualsIgnoreCase|_|) (s1:string) s2 = - if s1.Equals(s2, StringComparison.OrdinalIgnoreCase) - then Some () else None - - let (|OneOfIgnoreCase|_|) set str = - if Array.exists (fun s -> StringComparer.OrdinalIgnoreCase.Compare(s, str) = 0) set then Some() else None - - let msDateRegex = lazy Regex(@"^/Date\((-?\d+)([-+]\d+)?\)/$", RegexOptions.Compiled) - - let dateTimeStyles = DateTimeStyles.AllowWhiteSpaces ||| DateTimeStyles.RoundtripKind - - let ParseISO8601FormattedDateTime text cultureInfo = - match DateTime.TryParse(text, cultureInfo, dateTimeStyles) with - | true, d -> d |> Some - | false, _ -> None - -type TextConversions private() = - static member val DefaultMissingValues = [| "NaN"; "NA"; "N/A"; "#N/A"; ":"; "-"; "TBA"; "TBD" |] - - static member val DefaultNonCurrencyAdorners = [| '%'; '‰'; '‱' |] |> Set.ofArray - - static member val DefaultCurrencyAdorners = [| '¤'; '$'; '¢'; '£'; '¥'; '₱'; '﷼'; '₤'; '₭'; '₦'; '₨'; '₩'; '₮'; '€'; '฿'; '₡'; '៛'; '؋'; '₴'; '₪'; '₫'; '₹'; 'ƒ' |] |> Set.ofArray - - static member val private DefaultRemovableAdornerCharacters = - Set.union TextConversions.DefaultNonCurrencyAdorners TextConversions.DefaultCurrencyAdorners - - static member private RemoveAdorners (value : string) = - String(value.ToCharArray() |> Array.filter (not << TextConversions.DefaultRemovableAdornerCharacters.Contains)) - - static member AsString str = - if String.IsNullOrWhiteSpace str then None else Some str - - static member AsInteger cultureInfo text = - Int32.TryParse(TextConversions.RemoveAdorners text, NumberStyles.Integer, cultureInfo) |> asOption - - static member AsFloat missingValues useNoneForMissingValues cultureInfo (text:string) = - match text.Trim() with - | OneOfIgnoreCase missingValues -> if useNoneForMissingValues then None else Some Double.NaN - | _ -> Double.TryParse(text, NumberStyles.Any, cultureInfo) - |> asOption - |> Option.bind (fun f -> if useNoneForMissingValues && Double.IsNaN f then None else Some f) - - static member AsBoolean (text:string) = - match text.Trim() with - | StringEqualsIgnoreCase "true" | StringEqualsIgnoreCase "yes" | StringEqualsIgnoreCase "1" -> Some true - | StringEqualsIgnoreCase "false" | StringEqualsIgnoreCase "no" | StringEqualsIgnoreCase "0" -> Some false - | _ -> None - - static member AsDateTime cultureInfo (text:string) = - let matchesMS = msDateRegex.Value.Match (text.Trim()) - if matchesMS.Success then - matchesMS.Groups.[1].Value - |> Double.Parse - |> DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds - |> Some - else - match ParseISO8601FormattedDateTime text cultureInfo with - | Some d when d.Kind = DateTimeKind.Unspecified -> new DateTime(d.Ticks, DateTimeKind.Local) |> Some - | x -> x - - static member AsDateTimeOffset cultureInfo (text:string) = - let getTimeSpanFromHourMin (hourMin:int) = - let hr = (hourMin/100) |> float |> TimeSpan.FromHours - let min = (hourMin%100) |> float |> TimeSpan.FromMinutes - hr.Add min - - let offset str = - match Int32.TryParse str with - | true, v -> getTimeSpanFromHourMin v |> Some - | false, _ -> None - - let matchesMS = msDateRegex.Value.Match (text.Trim()) - if matchesMS.Success && matchesMS.Groups.[2].Success && matchesMS.Groups.[2].Value.Length = 5 then - match offset matchesMS.Groups.[2].Value with - | Some ofst -> - matchesMS.Groups.[1].Value - |> Double.Parse - |> DateTimeOffset(1970, 1, 1, 0, 0, 0, ofst).AddMilliseconds - |> Some - | None -> None - else - match ParseISO8601FormattedDateTime text cultureInfo with - | Some d when d.Kind <> DateTimeKind.Unspecified -> - match DateTimeOffset.TryParse(text, cultureInfo, dateTimeStyles) with - | true, dto -> dto |> Some - | false, _ -> None - | _ -> None - - static member AsTimeSpan (cultureInfo: CultureInfo) (text:string) = - match TimeSpan.TryParse(text, cultureInfo) with - | true, t -> Some t - | _ -> None - - static member AsGuid (text:string) = - Guid.TryParse(text.Trim()) |> asOption - -module internal UnicodeHelper = - let getUnicodeSurrogatePair num = - let codePoint = num - 0x010000u - let HIGH_TEN_BIT_MASK = 0xFFC00u - let LOW_TEN_BIT_MASK = 0x003FFu - let leadSurrogate = (codePoint &&& HIGH_TEN_BIT_MASK >>> 10) + 0xD800u - let trailSurrogate = (codePoint &&& LOW_TEN_BIT_MASK) + 0xDC00u - char leadSurrogate, char trailSurrogate \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Client/Types.fs b/src/FSharp.Data.GraphQL.Client/Types.fs new file mode 100644 index 000000000..5b6dc2ec8 --- /dev/null +++ b/src/FSharp.Data.GraphQL.Client/Types.fs @@ -0,0 +1,156 @@ +/// The MIT License (MIT) +/// Copyright (c) 2016 Bazinga Technologies Inc + +namespace FSharp.Data.GraphQL + +open System +open System.IO +open System.Text.Json +open System.Collections +open System.Collections.Generic +open System.ComponentModel +open FSharp.Data.GraphQL.Client + + +/// The base type for all GraphQLProvider upload types. +/// Upload types are used in GraphQL multipart request spec, mostly for file uploading features. +type Upload (stream : Stream, fileName : string, ?contentType : string, ?ownsStream : bool) = + new(bytes : byte [], fileName, ?contentType) = + let stream = new MemoryStream(bytes) + match contentType with + | Some ct -> new Upload(stream, fileName, ct, true) + | None -> new Upload(stream, fileName, ownsStream = true) + + /// Gets the stream associated to this Upload type. + member __.Stream = stream + + /// Gets the content type of this Upload type. + member __.ContentType = + match contentType with + | Some ct -> ct + | None -> + let ext = Path.GetExtension(fileName) + match MimeTypes.dict.Force().TryGetValue(ext) with + | (true, mime) -> mime + | _ -> "application/octet-stream" + + /// Gets the name of the file which contained on the stream. + member __.FileName = fileName + + /// Gets a boolean value indicating if this Upload type owns the stream associated with it. + /// If true, it will dispose the stream when this Upload type is disposed. + member __.OwnsStream = defaultArg ownsStream false + + interface IDisposable with + member x.Dispose() = if x.OwnsStream then x.Stream.Dispose() + + +/// The base type for all GraphQLProvider provided enum types. +type EnumBase (name : string, value : string) = + /// Gets the name of the provided enum type. + member __.GetName() = name + + /// Gets the value of the provided enum type. + member __.GetValue() = value + + override x.ToString() = x.GetValue() + + member x.Equals(other : EnumBase) = + x.GetName() = other.GetName() && x.GetValue() = other.GetValue() + + override x.Equals(other : obj) = + match other with + | :? EnumBase as other -> x.Equals(other) + | _ -> false + + override x.GetHashCode() = x.GetName().GetHashCode() ^^^ x.GetValue().GetHashCode() + + interface IEquatable with + member x.Equals(other) = x.Equals(other) + + +/// The base type for all GraphQLProvider provided record types. +type RecordBase (typeName: string, keyValuePairs: seq>) = + let properties = + keyValuePairs + |> Seq.map (|KeyValue|) + |> Map.ofSeq + + member _.GetName () = typeName + + member _.GetProperty(name: string) = + match properties.TryGetValue(name) with + | true, value -> value + | false, _ -> failwithf "No property with '%s' found for type '%s'" name typeName + + member _.TryGetProperty(name: string) = + Map.tryFind name properties + + member __.GetProperties () = + properties + + member __.ToDictionary () = + keyValuePairs + |> Seq.map (|KeyValue|) + |> readOnlyDict + + override this.ToString() = + (properties :> seq>).ToString() + + override __.GetHashCode () = + struct(typeName, properties).GetHashCode() + + override __.Equals(other) = + match other with + | :? RecordBase as r -> r.GetName() = typeName && r.GetProperties() = properties + | _ -> false + + interface seq> with + member _.GetEnumerator () = + (properties :> IEnumerable<_>).GetEnumerator() + member _.GetEnumerator () = + (properties :> IEnumerable).GetEnumerator() + + interface IEquatable with + member x.Equals(other) = x.Equals(other) + +type OperationError = + { Message: string + Path: obj [] } + +type OperationResult = + { Data: RecordBase option + Errors: OperationError [] + CustomData : Map } + +/// The base type for all GraphQLProvider operation result provided types. +type OperationResultBase (responseStream: Stream, parser: JsonElement -> OperationResult, operationTypeName : string) = + let response = + use document = JsonDocument.Parse(responseStream) + parser document.RootElement + + /// + [] + [] + member __.RawData = response.Data + + /// Gets all the errors returned by the operation on the server. + member __.Errors = response.Errors + + /// Gets all the custom data returned by the operation on server as a map of names and values. + member __.CustomData = response.CustomData + + member x.Equals(other : OperationResultBase) = + other.RawData <> response.Data + + override x.Equals(other : obj) = + match other with + | :? OperationResultBase as other -> x.Equals(other) + | _ -> false + + override _.GetHashCode() = response.GetHashCode() + +/// The base type for al GraphQLProvider operation provided types. +type OperationBase (query : string) = + /// Gets the query string of the operation. + member __.Query = query diff --git a/src/FSharp.Data.GraphQL.Client/Upload.fs b/src/FSharp.Data.GraphQL.Client/Upload.fs deleted file mode 100644 index ddeabdb21..000000000 --- a/src/FSharp.Data.GraphQL.Client/Upload.fs +++ /dev/null @@ -1,40 +0,0 @@ -/// The MIT License (MIT) -/// Copyright (c) 2016 Bazinga Technologies Inc - -namespace FSharp.Data.GraphQL - -open System -open System.IO -open FSharp.Data.GraphQL.Client - -/// The base type for all GraphQLProvider upload types. -/// Upload types are used in GraphQL multipart request spec, mostly for file uploading features. -type Upload (stream : Stream, fileName : string, ?contentType : string, ?ownsStream : bool) = - new(bytes : byte [], fileName, ?contentType) = - let stream = new MemoryStream(bytes) - match contentType with - | Some ct -> new Upload(stream, fileName, ct, true) - | None -> new Upload(stream, fileName, ownsStream = true) - - /// Gets the stream associated to this Upload type. - member __.Stream = stream - - /// Gets the content type of this Upload type. - member __.ContentType = - match contentType with - | Some ct -> ct - | None -> - let ext = Path.GetExtension(fileName) - match MimeTypes.dict.Force().TryGetValue(ext) with - | (true, mime) -> mime - | _ -> "application/octet-stream" - - /// Gets the name of the file which contained on the stream. - member __.FileName = fileName - - /// Gets a boolean value indicating if this Upload type owns the stream associated with it. - /// If true, it will dispose the stream when this Upload type is disposed. - member __.OwnsStream = defaultArg ownsStream false - - interface IDisposable with - member x.Dispose() = if x.OwnsStream then x.Stream.Dispose() \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Client/paket.references b/src/FSharp.Data.GraphQL.Client/paket.references index 3d7ef45ed..f1298333d 100644 --- a/src/FSharp.Data.GraphQL.Client/paket.references +++ b/src/FSharp.Data.GraphQL.Client/paket.references @@ -1,4 +1,5 @@ group Common +System.Text.Json FSharp.Core FParsec Microsoft.Extensions.Http \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Server.AspNet/AssemblyInfo.fs b/src/FSharp.Data.GraphQL.Server.AspNet/AssemblyInfo.fs new file mode 100644 index 000000000..e76544d03 --- /dev/null +++ b/src/FSharp.Data.GraphQL.Server.AspNet/AssemblyInfo.fs @@ -0,0 +1,17 @@ +// Auto-Generated by FAKE; do not edit +namespace System +open System.Reflection + +[] +[] +[] +[] +[] +do () + +module internal AssemblyVersionInformation = + let [] AssemblyTitle = "FSharp.Data.GraphQL.Server.AspNet" + let [] AssemblyProduct = "FSharp.Data.GraphQL" + let [] AssemblyDescription = "FSharp implementation of Facebook GraphQL query language" + let [] AssemblyVersion = "1.0.8" + let [] AssemblyFileVersion = "1.0.8" diff --git a/src/FSharp.Data.GraphQL.Server.AspNet/FSharp.Data.GraphQL.Server.AspNet.fsproj b/src/FSharp.Data.GraphQL.Server.AspNet/FSharp.Data.GraphQL.Server.AspNet.fsproj new file mode 100644 index 000000000..9c868909c --- /dev/null +++ b/src/FSharp.Data.GraphQL.Server.AspNet/FSharp.Data.GraphQL.Server.AspNet.fsproj @@ -0,0 +1,16 @@ + + + netstandard2.0 + preview + + + + + + + + + + + + \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Server.AspNet/FSharp.Data.GraphQL.Server.AspNet.fsproj.paket.template b/src/FSharp.Data.GraphQL.Server.AspNet/FSharp.Data.GraphQL.Server.AspNet.fsproj.paket.template new file mode 100644 index 000000000..60af8a9fe --- /dev/null +++ b/src/FSharp.Data.GraphQL.Server.AspNet/FSharp.Data.GraphQL.Server.AspNet.fsproj.paket.template @@ -0,0 +1,24 @@ +type project +id FSharp.Data.GraphQL.Server.AspNet +authors Bazinga Technologies Inc +projectUrl https://github.com/bazingatechnologies/FSharp.Data.GraphQL +licenseUrl https://github.com/bazingatechnologies/FSharp.Data.GraphQL/blob/dev/LICENSE.txt +requireLicenseAcceptance false +copyright Copyright (c) 2016 Bazinga Technologies Inc +tags + FSharp GraphQL Relay React AspNet +summary + Asp.Net integration for FSharp.Data.GraphQL.Server +description + Asp.Net integration for FSharp.Data.GraphQL.Server +dependencies + framework: netstandard2.0 + FSharp.Core >= LOCKEDVERSION-Common + System.Text.Json >= LOCKEDVERSION-AspNetLib + Microsoft.AspNetCore.Http >= LOCKEDVERSION-AspNetLib + Microsoft.AspNetCore.Http.Abstractions >= LOCKEDVERSION-AspNetLib + Microsoft.AspNetCore.Http.Extensions >= LOCKEDVERSION-AspNetLib + FSharp.Data.GraphQL.Shared >= CURRENTVERSION + FSharp.Data.GraphQL.Server >= CURRENTVERSION +files + bin/Release/netstandard2.0/FSharp.Data.GraphQL.Server.AspNet.dll ==> lib/netstandard2.0 \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Server.AspNet/GraphQL.fs b/src/FSharp.Data.GraphQL.Server.AspNet/GraphQL.fs new file mode 100644 index 000000000..0b9b9ca11 --- /dev/null +++ b/src/FSharp.Data.GraphQL.Server.AspNet/GraphQL.fs @@ -0,0 +1,240 @@ +namespace FSharp.Data.GraphQL.Server.AspNet + +open System +open System.IO +open System.Net +open System.Text.Json +open System.Threading.Tasks +open System.Security.Cryptography + +open FSharp.Data.GraphQL +open FSharp.Data.GraphQL.Types + +open Microsoft.AspNetCore.Builder +open Microsoft.AspNetCore.Http +open Microsoft.Net.Http.Headers +open Microsoft.Extensions.Primitives +open Microsoft.AspNetCore.WebUtilities +open Microsoft.AspNetCore.Http.Extensions + +[] +module private MultipartRequest = + let getBoundy (contentType: MediaTypeHeaderValue) (lengthLimit: int) = + let boundary = HeaderUtilities.RemoveQuotes(contentType.Boundary) + if System.String.IsNullOrEmpty boundary.Value then + raise <| InvalidDataException("Missing content-type boundary.") + elif boundary.Value.Length > lengthLimit then + raise <| InvalidDataException(sprintf "Multiparty boundary length limit %i exceeded" lengthLimit) + else + boundary.Value + + let isMultipartContentType (contentType: string) = + not(System.String.IsNullOrEmpty contentType) && contentType.IndexOf("multipart/", StringComparison.OrdinalIgnoreCase) >= 0 + + let (|FormContent|FileContent|NoContent|) (section: MultipartSection) = + match ContentDispositionHeaderValue.TryParse(StringSegment section.ContentDisposition) with + | true, contentDisposition when not(isNull contentDisposition) -> + if contentDisposition.IsFormDisposition() + then FormContent contentDisposition + else FileContent contentDisposition + | _ -> + NoContent + + +module private ExecutionHandler = + // crypto stream that doesn't close underlying stream + type NonClosingCryptoStream(stream, transform, mode) = + inherit CryptoStream(stream, transform, mode) + override this.Dispose(disposing) = + if (not this.HasFlushedFinalBlock) then + this.FlushFinalBlock() + base.Dispose(false) + + let bin2hex bytes = + let byteToChar b = char(if b > 9uy then b + 0x37uy else b + 0x30uy) + new string [| for byte in bytes do byteToChar(byte >>> 4); byteToChar(byte &&& 0xFuy) |] + + let tryReadSection (reader: MultipartReader) = async { + let! result = reader.ReadNextSectionAsync() |> Async.AwaitTask + return Option.ofObj result + } + + let tryReadForm (reader: MultipartReader) = async { + match! tryReadSection reader with + | Some(FormContent contentDisposition & section) -> + let form = section.AsFormDataSection() + let! value = form.GetValueAsync() |> Async.AwaitTask + return Some(contentDisposition.Name.Value, value) + | _ -> + return None + } + + let tryReadFile (reader: MultipartReader) = async { + match! tryReadSection reader with + | Some(FileContent contentDisposition & section) -> + let fileName = WebUtility.HtmlEncode(contentDisposition.FileName.Value) + let filepath = Path.GetTempFileName() + let cryptoHash = SHA256.Create() + let options = FileOptions.Asynchronous ||| FileOptions.SequentialScan + let stream = new FileStream(filepath, FileMode.Create, FileAccess.ReadWrite, FileShare.None, 4096, options) + use hash = new NonClosingCryptoStream(stream, cryptoHash, CryptoStreamMode.Write) + do! section.Body.CopyToAsync(hash) |> Async.AwaitTask + hash.FlushFinalBlock() + stream.Position <- 0L + let file = + { Name = fileName; ContentType = section.ContentType; Size = stream.Length |> int + Path = filepath; Content = stream; Hash = bin2hex cryptoHash.Hash } + return Some(contentDisposition.Name.Value, file) + | _ -> + return None + } + + let readFiles (reader: MultipartReader) = async { + let rec loop files = async { + match! tryReadFile reader with + | Some file -> return! loop (file::files) + | None -> return Map.ofList files + } + return! loop [] + } + + let readMapping (json: string) (fileMapping: Map) = + let bytes = System.Text.Encoding.UTF8.GetBytes(json) + let readonlySpan = ReadOnlySpan(bytes) + let reader = Utf8JsonReader(readonlySpan) + let mapping = ResizeArray() + reader.Read() |> ignore + match reader.TokenType with + | JsonTokenType.StartObject -> + reader.Read() |> ignore + while reader.TokenType <> JsonTokenType.EndObject do + let fileKey = reader.GetString() + match Map.tryFind fileKey fileMapping with + | Some file -> + reader.Read() |> ignore + match reader.TokenType with + | JsonTokenType.StartArray -> + reader.Read() |> ignore + while reader.TokenType <> JsonTokenType.EndArray do + let path = reader.GetString() + mapping.Add (path, file) + reader.Read() |> ignore + reader.Read() |> ignore + | invalidToken -> + let message = sprintf "Expected 'StartArray' but received json token '%A'" invalidToken + raise <| invalidOp message + | None -> + failwithf "Mapped file %s not found in request" fileKey + Map.ofSeq mapping + | invalidToken -> + let message = sprintf "Expected PropertyName or EndObject for property mapping but received json token '%A'" invalidToken + raise <| invalidOp message + + let readOperation (cache: Json.TypeReaderCache) (executor: Executor<'T>) (element: JsonElement) (replacements: Map) (index: int option) = + match element.ValueKind with + | JsonValueKind.Object -> + let queryText = + match element.TryGetProperty("query") with + | true, queryElement -> queryElement.GetString() + | false, _ -> Introspection.IntrospectionQuery + let variablesElement = + match element.TryGetProperty("variables") with + | true, value when value.ValueKind <> JsonValueKind.Null -> Some value + | _ -> None + let plan = executor.CreateExecutionPlan(queryText) + let variables = Json.readVariables cache replacements index variablesElement plan.Variables + { ExecutionPlan = plan; Variables = variables } + | invalidElementKind -> + let message = sprintf "Expected 'Object' element kind but received '%A'" invalidElementKind + raise <| invalidOp message + + let readOperations (cache: Json.TypeReaderCache) (executor: Executor<'T>) (json: string) (replacements: Map) = + use document = JsonDocument.Parse(json) + match document.RootElement.ValueKind with + | JsonValueKind.Array -> + let operations = + document.RootElement.EnumerateArray() + |> Seq.mapi(fun i operation -> readOperation cache executor operation replacements (Some i)) + |> Seq.toList + Batch operations + | _ -> + let operation = readOperation cache executor document.RootElement replacements None + Single operation + + let serializeResponse (ctx: HttpContext) (serializerOptions: JsonSerializerOptions) (body: 'T) = async { + let outputStream = ctx.Response.Body + do! JsonSerializer.SerializeAsync(outputStream, body, serializerOptions) |> Async.AwaitTask + } + + let processMultipartQuery (cache: Json.TypeReaderCache) (executor: Executor<'T>) (root: 'T) (ctx: HttpContext) (serializerOptions: JsonSerializerOptions) = async { + let boundary = ctx.Request.GetMultipartBoundary() + let reader = MultipartReader(boundary, ctx.Request.Body) + let! operationPart = tryReadForm reader + let! mappingPart = tryReadForm reader + let! fileParts = readFiles reader + match mappingPart with + | Some("map", mappingJson) -> + match operationPart with + | Some("operations", operationsJson) -> + let replacements = readMapping mappingJson fileParts + match readOperations cache executor operationsJson replacements with + | Single request -> + let! response = executor.AsyncExecute(request.ExecutionPlan, data = root, variables = request.Variables) + do! serializeResponse ctx serializerOptions response + | Batch requests -> + let hasMutation = requests |> List.exists (fun req -> req.ExecutionPlan.Operation.OperationType = Ast.OperationType.Mutation) + let operations = requests |> List.map(fun req -> executor.AsyncExecute(req.ExecutionPlan, data = root, variables = req.Variables)) + let! responses = if hasMutation then Async.Sequential operations else Async.Parallel operations + do! serializeResponse ctx serializerOptions responses + | _ -> + let message = "Expected operations as first segment of multipart request" + return raise <| invalidOp message + | _ -> + let message = "Expected mapping as second segment of multipart request" + return raise <| invalidOp message + } + + let readBody (ctx: HttpContext) = async { + use reader = new StreamReader(ctx.Request.Body, Text.Encoding.UTF8) + return! reader.ReadToEndAsync() |> Async.AwaitTask + } + + let processQuery (cache: Json.TypeReaderCache) (executor: Executor<'T>) (root: 'T) (ctx: HttpContext) (serializerOptions: JsonSerializerOptions) = async { + let! body = readBody ctx + let! response = + if System.String.IsNullOrWhiteSpace body then + executor.AsyncExecute(Introspection.IntrospectionQuery, root) + else + async { + use document = JsonDocument.Parse(body) + let request = readOperation cache executor document.RootElement Map.empty None + return! executor.AsyncExecute(request.ExecutionPlan, data = root, variables = request.Variables) + } + let outputStream = ctx.Response.Body + do! JsonSerializer.SerializeAsync(outputStream, response, serializerOptions) |> Async.AwaitTask + } + + let queryHandler (buildArguments: HttpContext -> Async<'T>) (executor: Executor<'T>) (readerCache: Json.TypeReaderCache) = + let graphQLSerializationOptions = JsonSerializerOptions() + graphQLSerializationOptions.Converters.Add(Json.GQLResponseConverter()) + graphQLSerializationOptions.Converters.Add(Json.NameValueLookupConverter()) + fun (ctx: HttpContext) -> + async { + ctx.Response.Headers.Add("Content-Type", StringValues("application/json")) + let! root = ctx |> buildArguments + let execute = + if isMultipartContentType ctx.Request.ContentType + then processMultipartQuery + else processQuery + do! execute readerCache executor root ctx graphQLSerializationOptions + } |> Async.StartAsTask :> Task + +[] +module ApplicationBuilderExtensions = + type IApplicationBuilder with + member builder.UseGraphQL(executor: Executor<'T>, buildArguments: HttpContext -> Async<'T>, ?path: string) = + let urlPath = defaultArg path "/graphql" + let readerCache = Json.TypeReaderCache() + let graphQLHandler = ExecutionHandler.queryHandler buildArguments executor readerCache + builder.Map(PathString(urlPath.TrimEnd('/')), (fun (builder:IApplicationBuilder) -> + builder.Run(RequestDelegate(graphQLHandler)))) \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Server.AspNet/Json.fs b/src/FSharp.Data.GraphQL.Server.AspNet/Json.fs new file mode 100644 index 000000000..c69f24b7d --- /dev/null +++ b/src/FSharp.Data.GraphQL.Server.AspNet/Json.fs @@ -0,0 +1,256 @@ +module internal FSharp.Data.GraphQL.Server.AspNet.Json + +open System +open System.Reflection +open System.Text.Json +open System.Text.Json.Serialization +open System.Collections.Concurrent + +open FSharp.Reflection +open FSharp.Data.GraphQL.Execution +open FSharp.Data.GraphQL.Types +open FSharp.Data.GraphQL.Types.Patterns + +type IJsonVariableReader = + abstract IsNullable : bool + abstract Read : replacements: Map * path: string * element: JsonElement -> obj + +type TypeReaderCache = ConcurrentDictionary + +let (|SpecificTypeDef|_|) (def: TypeDef) (value: TypeDef) = + if def = value then Some() else None + +let combinePath (left: string) (right: string) = + sprintf "%s.%s" left right + +let toCamelCase (value: string) = + JsonNamingPolicy.CamelCase.ConvertName value + +let getOptionCtors (optionInnerType: Type) = + let optionType = typedefof> + let cases = FSharpType.GetUnionCases(optionType.MakeGenericType(optionInnerType)) + let none = FSharpValue.PreComputeUnionConstructor cases.[0] + let some = FSharpValue.PreComputeUnionConstructor cases.[1] + some, none + +let private getCallInfo = function + | Quotations.Patterns.Call(_, info, _) -> info + | q -> failwithf "Unexpected Quotation! %A" q + +let convertArray (items: seq) : 'T array= + items + |> Seq.cast<'T> + |> Array.ofSeq + +let convertList (items: seq) : 'T list = + items + |> Seq.cast<'T> + |> List.ofSeq + +let convertSet (items: seq) : 'T Set = + items + |> Seq.cast<'T> + |> Set.ofSeq + +let convertSeq (items: seq) : 'T seq = + items + |> Seq.cast<'T> + |> Array.ofSeq + :> seq<_> + +let rec getJsonReaderAux (input: InputDef) (cache: TypeReaderCache) = + match input with + | Nullable (Input(innerDef)) -> getNullableReader innerDef cache + | Scalar scalarDef -> getScalarReader scalarDef + | Enum enumDef -> getEnumReader enumDef + | List(Input(elementDef)) -> getListReader input elementDef cache + | InputObject inputObjectDef -> getInputObjectReader inputObjectDef cache + | typeDef -> raise(invalidOp(sprintf "Invalid input definition '%s'" typeDef.Type.Name)) +and getJsonReader (input: InputDef) (cache: ConcurrentDictionary) = + match cache.TryGetValue input with + | true, value -> + value + | false, _ -> + let readerFunc = lazy getJsonReaderAux input cache + let isNullable = input :? NullableDef + cache.GetOrAdd(input, (fun input -> + { new IJsonVariableReader with + member _.IsNullable = isNullable + member _.Read (replacements, path, element) = + readerFunc.Value replacements path element })) +and getNullableReader (innerDef: InputDef) (cache: TypeReaderCache) = + let innerReader = getJsonReader innerDef cache + let someCtor, noneCtor = getOptionCtors innerDef.Type + fun replacements path (element: JsonElement) -> + match element.ValueKind with + | JsonValueKind.Null -> + match Map.tryFind path replacements with + | Some file -> someCtor [| file :> obj |] + | None -> noneCtor [||] + | _ -> + someCtor [| innerReader.Read(replacements, path, element) |] +and getScalarReader (scalarDef: ScalarDef) = + match scalarDef with + | SpecificTypeDef SchemaDefinitions.Date -> + fun _ _ (element: JsonElement) -> element.GetDateTime() :> _ + | SpecificTypeDef SchemaDefinitions.Guid -> + fun _ _ (element: JsonElement) -> element.GetGuid() :> _ + | SpecificTypeDef SchemaDefinitions.Uri -> + fun _ _ (element: JsonElement) -> System.Uri(element.GetString()) :> _ + | SpecificTypeDef SchemaDefinitions.Boolean -> + fun _ _ (element: JsonElement) -> element.GetBoolean() :> _ + | SpecificTypeDef SchemaDefinitions.Float -> + fun _ _ (element: JsonElement) -> element.GetDouble() :> _ + | SpecificTypeDef SchemaDefinitions.Int -> + fun _ _ (element: JsonElement) -> element.GetInt32() :> _ + | SpecificTypeDef SchemaDefinitions.FileUpload -> + fun replacements path (element: JsonElement) -> + match Map.tryFind path replacements with + | Some file -> file :> _ + | None -> failwithf "Expected file upload at %s" path + | _ -> + fun _ _ (element: JsonElement) -> element.GetString() :> _ +and getEnumReader (enumDef: EnumDef) = + if enumDef.Type.IsEnum then + fun _ _ element -> + let value = element.GetString() + Enum.Parse(enumDef.Type, value, ignoreCase=true) + else + let flags = BindingFlags.NonPublic ||| BindingFlags.Public + let cases = FSharpType.GetUnionCases(enumDef.Type, flags) + let ctors = + cases + |> Seq.map(fun case -> case.Name.ToLowerInvariant(), FSharpValue.PreComputeUnionConstructor case) + |> Map.ofSeq + fun _ _ element -> + let value = element.GetString() + match Map.tryFind (value.ToLowerInvariant()) ctors with + | Some ctor -> ctor [||] + | None -> failwithf "Case '%s' does not match any Union constructors in '%s'" value enumDef.Type.Name +and getListReader (listDef: InputDef) (elementDef: InputDef) (cache: TypeReaderCache) = + if listDef.Type.IsGenericType then + let elementReader = getJsonReader elementDef cache + let genericType = listDef.Type.GetGenericTypeDefinition() + let collectionConverterMethInfo = + if genericType = typedefof> then getCallInfo <@ convertList Seq.empty @> + elif genericType = typedefof> then getCallInfo <@ convertArray Seq.empty @> + elif genericType = typedefof> then getCallInfo <@ convertSet Seq.empty @> + else getCallInfo <@ convertSeq Seq.empty @> + let genericMethInfo = collectionConverterMethInfo.GetGenericMethodDefinition() + let methInfo = genericMethInfo.MakeGenericMethod([|elementDef.Type|]) + let convertF v = methInfo.Invoke(null, [|v|]) + fun replacements path element -> + match element.ValueKind with + | JsonValueKind.Array -> + use enumerator = element.EnumerateArray() + enumerator + |> Seq.mapi(fun i value -> + let elementPath = combinePath path (i.ToString()) + elementReader.Read(replacements, elementPath, value) ) + |> convertF + | otherElement -> + failwithf "Expected array element but received '%A'" otherElement + else + failwithf "Unsupported GraphQL 'List' type '%s'" listDef.Type.Name +and getInputObjectReader (inputObject: InputObjectDef) (cache: TypeReaderCache) = + if FSharpType.IsRecord inputObject.Type then + let inputFieldReaders = + inputObject.Fields + |> Array.map (fun field -> field.Name.ToLowerInvariant(), (field, toCamelCase field.Name, getJsonReader field.TypeDef cache)) + |> Map.ofArray + let fieldReaders = + [ for field in FSharpType.GetRecordFields(inputObject.Type, true) do + let fieldName = field.Name.ToLowerInvariant() + match Map.tryFind fieldName inputFieldReaders with + | Some fieldReader -> fieldReader + | None -> failwithf "field '%s' exists on Record '%s' but not InputObject '%s'" field.Name inputObject.Type.Name inputObject.Name ] + let ctor = FSharpValue.PreComputeRecordConstructor(inputObject.Type, true) + fun replacements path element -> + match element.ValueKind with + | JsonValueKind.Object -> + let values = + [| for (field, name, reader) in fieldReaders do + let elementPath = combinePath path name + match Map.tryFind elementPath replacements with + | Some value -> + box value + | None -> + match element.TryGetProperty name, field.DefaultValue with + | (true, value), _ -> + reader.Read(replacements, elementPath, value) + | (false, _), Some(defaultValue) -> + box defaultValue + | (false, _), None when reader.IsNullable -> + match field.TypeDef with + | Nullable(Input(innerDef)) -> + let _, noneCtor = getOptionCtors innerDef.Type + noneCtor [||] + | _ -> + failwithf "Expected field '%s' to be a nullable type" field.Name + | (false, _), None -> + failwithf "Field '%s' is required, but not present in json object" field.Name |] + ctor values + | fieldKind -> + failwithf "Expected Json Object for InputObject '%s' but received '%A'" inputObject.Name fieldKind + else + failwithf "InputObject '%s' must be a Record type." inputObject.Type.Name + +let readVariable (cache: TypeReaderCache) (var: VarDef) (basePath: string) (element: JsonElement) (replacements: Map) = + let path = combinePath basePath var.Name + let reader = getJsonReader var.TypeDef cache + let variableValue = reader.Read(replacements, path, element) + (var.Name, variableValue) + +let readVariables (cache: TypeReaderCache) (replacements: Map) (index: int option) (variablesJson: JsonElement option) (vars: list) = + let basePath = + match index with + | Some idx -> combinePath (idx.ToString()) "variables" + | None -> "variables" + let tryReadProperty = + match variablesJson with + | Some variables -> + fun (name: string) -> + match variables.TryGetProperty(name) with + | true, value -> Some value + | false, _ -> None + | None -> + fun _ -> None + Map.ofList [ + for var in vars do + match tryReadProperty var.Name with + | Some element -> + readVariable cache var basePath element replacements + | None -> + match var.DefaultValue, var.TypeDef with + | Some _, _ -> () + | _, Nullable _ -> () + | _ -> failwithf "Variable '%s' is missing and there is no default value" var.Name + ] + +type NameValueLookupConverter () = + inherit JsonConverter() + override _.Read(reader: byref, typeToConvert: System.Type, options: JsonSerializerOptions) = + invalidOp "deserialization not supported" + + override _.Write(writer: Utf8JsonWriter, value: NameValueLookup, options: JsonSerializerOptions) = + writer.WriteStartObject() + for (KeyValue(key, data)) in value do + writer.WritePropertyName(key) + if isNull data then + writer.WriteNullValue() + else + JsonSerializer.Serialize(writer, data, data.GetType(), options) + writer.WriteEndObject() + +type GQLResponseConverter () = + inherit JsonConverter() + + override _.Read(reader: byref, typeToConvert: System.Type, options: JsonSerializerOptions) = + invalidOp "deserialization not supported" + + override _.Write(writer: Utf8JsonWriter, value: GQLResponse, options: JsonSerializerOptions) = + match value with + | Direct(data, errors) -> + JsonSerializer.Serialize(writer, data, typeof, options) + | _ -> + writer.WriteNull("data") diff --git a/src/FSharp.Data.GraphQL.Server.AspNet/Types.fs b/src/FSharp.Data.GraphQL.Server.AspNet/Types.fs new file mode 100644 index 000000000..ed149e207 --- /dev/null +++ b/src/FSharp.Data.GraphQL.Server.AspNet/Types.fs @@ -0,0 +1,50 @@ +namespace FSharp.Data.GraphQL.Types + +open System.IO +open FSharp.Data.GraphQL.Execution +open FSharp.Data.GraphQL.Types + +type GraphQLQuery = + { ExecutionPlan : ExecutionPlan + Variables : Map } + +type GraphQLQueryRequest = + | Single of GraphQLQuery + | Batch of GraphQLQuery list + +type WebSocketClientMessage = + | ConnectionInit of token : string option * session : string option * scheme : string option + | ConnectionTerminate + | Start of id : string * payload : GraphQLQuery + | Stop of id : string + | ParseError of id : string option * err : string + +type WebSocketServerMessage = + | ConnectionAck + | ConnectionError of err : string + | Data of id : string * payload : Output + | Error of id : string option * err : string + | Complete of id : string + + +type FileUpload = + { Name : string + ContentType : string + Size : int + Content : Stream + Path : string + Hash : string } + +[] +module SchemaDefinitions = + let FileUpload = + Define.Scalar( + name = "Upload", + description = "The `Upload` scalar type represents a file upload.", + coerceValue = (fun value -> + match value with + | :? FileUpload as upload -> Some upload + | _ -> None), + coerceInput = fun value -> + raise <| invalidOp("Upload cannot be coerced from AST input.") + ) \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Server.AspNet/paket.references b/src/FSharp.Data.GraphQL.Server.AspNet/paket.references new file mode 100644 index 000000000..31e1f774b --- /dev/null +++ b/src/FSharp.Data.GraphQL.Server.AspNet/paket.references @@ -0,0 +1,8 @@ +group Common +FSharp.Core + +group AspNetLib +System.Text.Json +Microsoft.AspNetCore.Http +Microsoft.AspNetCore.Http.Abstractions +Microsoft.AspNetCore.Http.Extensions \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Server/Relay/Connections.fs b/src/FSharp.Data.GraphQL.Server/Relay/Connections.fs index 60e5869cb..cedb1429f 100644 --- a/src/FSharp.Data.GraphQL.Server/Relay/Connections.fs +++ b/src/FSharp.Data.GraphQL.Server/Relay/Connections.fs @@ -126,7 +126,7 @@ module Definitions = fields = [ Define.Field("totalCount", Nullable Int, """A count of the total number of objects in this connection, ignoring pagination. This allows a client to fetch the first five objects by passing \"5\" as the argument to `first`, then fetch the total count so it could display \"5 of 83\", for example. In cases where we employ infinite scrolling or don't have an exact count of entries, this field will return `null`.""", fun _ conn -> conn.TotalCount) Define.Field("pageInfo", PageInfo, "Information to aid in pagination.", fun _ conn -> conn.PageInfo) - Define.Field("edges", ListOf(EdgeOf nodeType), "Information to aid in pagination.", fun _ conn -> conn.Edges)]) + Define.Field("edges", SeqOf(EdgeOf nodeType), "Information to aid in pagination.", fun _ conn -> conn.Edges)]) [] module Connection = diff --git a/src/FSharp.Data.GraphQL.Server/Schema.fs b/src/FSharp.Data.GraphQL.Server/Schema.fs index ebde61e27..ef9f6167f 100644 --- a/src/FSharp.Data.GraphQL.Server/Schema.fs +++ b/src/FSharp.Data.GraphQL.Server/Schema.fs @@ -31,16 +31,17 @@ type private ChannelBag() = tags |> Seq.iter adder upcast channel member __.GetAll() = - untagged |> Seq.map id + seq { yield! untagged } + member __.GetByTag(tag) = match tagged.TryGetValue(tag) with | false, _ -> Seq.empty - | true, channels -> channels |> Seq.map id + | true, channels -> seq { yield! channels } type private SubscriptionManager() = let subscriptions = Dictionary() member __.Add(subscription : Subscription) = - subscriptions.Add(subscription.Name, (subscription, new ChannelBag())) + subscriptions.Add(subscription.Name, (subscription, ChannelBag())) member __.TryGet(subscriptionName) = match subscriptions.TryGetValue(subscriptionName) with | true, sub -> Some sub @@ -64,7 +65,7 @@ type SchemaConfig = } /// Returns the default Subscription Provider, backed by Observable streams. static member DefaultSubscriptionProvider() = - let subscriptionManager = new SubscriptionManager() + let subscriptionManager = SubscriptionManager() { new ISubscriptionProvider with member __.AsyncRegister (subscription : Subscription) = async { return subscriptionManager.Add(subscription) } diff --git a/src/FSharp.Data.GraphQL.Server/Values.fs b/src/FSharp.Data.GraphQL.Server/Values.fs index fbc646358..28faf9e45 100644 --- a/src/FSharp.Data.GraphQL.Server/Values.fs +++ b/src/FSharp.Data.GraphQL.Server/Values.fs @@ -10,6 +10,7 @@ open System.Collections.Generic open FSharp.Data.GraphQL.Ast open FSharp.Data.GraphQL.Types open FSharp.Data.GraphQL.Types.Patterns +open FSharp.Data.GraphQL.Helpers /// Tries to convert type defined in AST into one of the type defs known in schema. let inline tryConvertAst schema ast = @@ -112,7 +113,11 @@ let rec private coerceVariableValue isNullable typedef (vardef: VarDef) (input: | Some res -> res | Nullable (Input innerdef) -> let some, none = ReflectionHelper.optionOfType innerdef.Type - let coerced = coerceVariableValue true innerdef vardef input errMsg + let nullableInput = + match input with + | ObjectOption inner -> inner + | _ -> input + let coerced = coerceVariableValue true innerdef vardef nullableInput errMsg if coerced <> null then let s = some coerced diff --git a/src/FSharp.Data.GraphQL.Shared/Introspection.fs b/src/FSharp.Data.GraphQL.Shared/Introspection.fs index 296d0cc2e..f1a813e7c 100644 --- a/src/FSharp.Data.GraphQL.Shared/Introspection.fs +++ b/src/FSharp.Data.GraphQL.Shared/Introspection.fs @@ -7,11 +7,6 @@ module FSharp.Data.GraphQL.Introspection open FSharp.Data.GraphQL.Types open FSharp.Data.GraphQL.Types.Introspection open FSharp.Data.GraphQL.Extensions -open System.Reflection - -let internal getFieldValue name o = - let property = o.GetType().GetTypeInfo().GetDeclaredProperty(name, ignoreCase=true) - if isNull property then null else property.GetValue(o, null) /// Common GraphQL query that may be used to retrieve overall data /// about schema type system itself. @@ -183,7 +178,7 @@ let rec __Type = Define.Field("kind", __TypeKind, fun _ t -> t.Kind) Define.Field("name", Nullable String, resolve = fun _ t -> t.Name) Define.Field("description", Nullable String, resolve = fun _ t -> t.Description) - Define.Field("fields", Nullable (ListOf __Field), + Define.Field("fields", Nullable (SeqOf __Field), args = [Define.Input("includeDeprecated", Boolean, false) ], resolve = fun ctx t -> match t.Name with @@ -193,19 +188,19 @@ let rec __Type = match ctx.TryArg "includeDeprecated" with | Some true -> found.Fields |> Option.map Array.toSeq | _ -> found.Fields |> Option.map (fun x -> upcast Array.filter (fun f -> not f.IsDeprecated) x)) - Define.Field("interfaces", Nullable (ListOf __Type), resolve = fun ctx t -> + Define.Field("interfaces", Nullable (SeqOf __Type), resolve = fun ctx t -> match t.Name with | None -> None | Some name -> let found = findIntrospected ctx name found.Interfaces |> Option.map Array.toSeq ) - Define.Field("possibleTypes", Nullable (ListOf __Type), resolve = fun ctx t -> + Define.Field("possibleTypes", Nullable (SeqOf __Type), resolve = fun ctx t -> match t.Name with | None -> None | Some name -> let found = findIntrospected ctx name found.PossibleTypes |> Option.map Array.toSeq) - Define.Field("enumValues", Nullable (ListOf __EnumValue), + Define.Field("enumValues", Nullable (SeqOf __EnumValue), args = [Define.Input("includeDeprecated", Boolean, false) ], resolve = fun ctx t -> match t.Name with | None -> None @@ -214,7 +209,7 @@ let rec __Type = match ctx.TryArg "includeDeprecated" with | None | Some false -> found.EnumValues |> Option.map Array.toSeq | Some true -> found.EnumValues |> Option.map (fun x -> upcast (x |> Array.filter (fun f -> not f.IsDeprecated)))) - Define.Field("inputFields", Nullable (ListOf __InputValue), resolve = fun ctx t -> + Define.Field("inputFields", Nullable (SeqOf __InputValue), resolve = fun ctx t -> match t.Name with | None -> None | Some name -> @@ -248,7 +243,7 @@ and __Field = [ Define.Field("name", String, fun _ f -> f.Name) Define.Field("description", Nullable String, fun _ f -> f.Description) - Define.Field("args", ListOf __InputValue, fun _ f -> f.Args) + Define.Field("args", ArrayOf __InputValue, fun _ f -> f.Args) Define.Field("type", __Type, fun _ f -> f.Type) Define.Field("isDeprecated", Boolean, resolve = fun _ f -> f.IsDeprecated) Define.Field("deprecationReason", Nullable String, fun _ f -> f.DeprecationReason) @@ -286,8 +281,8 @@ and __Directive = [ Define.Field("name", String, resolve = fun _ directive -> directive.Name) Define.Field("description", Nullable String, resolve = fun _ directive -> directive.Description) - Define.Field("locations", ListOf __DirectiveLocation, resolve = fun _ directive -> directive.Locations) - Define.Field("args", ListOf __InputValue, resolve = fun _ directive -> directive.Args) + Define.Field("locations", ArrayOf __DirectiveLocation, resolve = fun _ directive -> directive.Locations) + Define.Field("args", ArrayOf __InputValue, resolve = fun _ directive -> directive.Args) Define.Field("onOperation", Boolean, resolve = fun _ d -> d.Locations |> Seq.exists (oneOf [| DirectiveLocation.QUERY; DirectiveLocation.MUTATION; DirectiveLocation.SUBSCRIPTION |])) Define.Field("onFragment", Boolean, resolve = fun _ d -> d.Locations |> Seq.exists (oneOf [| DirectiveLocation.FRAGMENT_SPREAD; DirectiveLocation.INLINE_FRAGMENT; DirectiveLocation.FRAGMENT_DEFINITION |])) Define.Field("onField", Boolean, resolve = fun _ d -> d.Locations |> Seq.exists (oneOf [| DirectiveLocation.FIELD |])) @@ -302,9 +297,9 @@ and __Schema = description = "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.", fieldsFn = fun () -> [ - Define.Field("types", ListOf __Type, description = "A list of all types supported by this server.", resolve = fun _ schema -> schema.Types |> Array.map IntrospectionTypeRef.Named) + Define.Field("types", ArrayOf __Type, description = "A list of all types supported by this server.", resolve = fun _ schema -> schema.Types |> Array.map IntrospectionTypeRef.Named) Define.Field("queryType", __Type, description = "The type that query operations will be rooted at.", resolve = fun _ schema -> schema.QueryType) Define.Field("mutationType", Nullable __Type, description = "If this server supports mutation, the type that mutation operations will be rooted at.", resolve = fun _ schema -> schema.MutationType) Define.Field("subscriptionType", Nullable __Type, description = "If this server support subscription, the type that subscription operations will be rooted at.", resolve = fun _ schema -> schema.SubscriptionType) - Define.Field("directives", ListOf __Directive, description = "A list of all directives supported by this server.", resolve = fun _ schema -> schema.Directives) + Define.Field("directives", ArrayOf __Directive, description = "A list of all directives supported by this server.", resolve = fun _ schema -> schema.Directives) ]) \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs b/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs index 4ebfd7195..cc1d199ab 100644 --- a/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs +++ b/src/FSharp.Data.GraphQL.Shared/TypeSystem.fs @@ -2518,7 +2518,20 @@ module SchemaDefinitions = /// Wraps a GraphQL type definition, allowing defining field/argument /// to take collection of provided value. - let ListOf(innerDef : #TypeDef<'Val>) : ListOfDef<'Val, 'Seq> = upcast { ListOfDefinition.OfType = innerDef } + let ListOf(innerDef : #TypeDef<'Val>) : ListOfDef<'Val, 'Val list> = upcast { ListOfDefinition.OfType = innerDef } + + /// Wraps a GraphQL type definition, allowing defining field/argument + /// to take collection of provided value. + let SeqOf(innerDef : #TypeDef<'Val>) : ListOfDef<'Val, 'Val seq> = upcast { ListOfDefinition.OfType = innerDef } + + /// Wraps a GraphQL type definition, allowing defining field/argument + /// to take collection of provided value. + let ArrayOf(innerDef : #TypeDef<'Val>) : ListOfDef<'Val, 'Val array> = upcast { ListOfDefinition.OfType = innerDef } + + + /// Wraps a GraphQL type definition, allowing defining field/argument + /// to take collection of provided value. + let SetOf(innerDef : #TypeDef<'Val>) : ListOfDef<'Val, 'Val Set> = upcast { ListOfDefinition.OfType = innerDef } let private ignoreInputResolve (_ : unit) (input : 'T) = () diff --git a/tests/FSharp.Data.GraphQL.Benchmarks/AsyncSchema.fs b/tests/FSharp.Data.GraphQL.Benchmarks/AsyncSchema.fs index cab516964..41de4d32b 100644 --- a/tests/FSharp.Data.GraphQL.Benchmarks/AsyncSchema.fs +++ b/tests/FSharp.Data.GraphQL.Benchmarks/AsyncSchema.fs @@ -43,7 +43,7 @@ module AsyncSchemaDefinition = fieldsFn = fun () -> [ Define.AsyncField("id", String, resolve = fun _ person -> delay person.Id) Define.AsyncField("name", Nullable String, resolve = fun _ person -> delay person.Name) - Define.AsyncField("friends", Nullable(ListOf(Nullable Person)), + Define.AsyncField("friends", Nullable(SeqOf(Nullable Person)), resolve = fun _ person -> person.Friends |> List.map getPerson diff --git a/tests/FSharp.Data.GraphQL.Benchmarks/Schema.fs b/tests/FSharp.Data.GraphQL.Benchmarks/Schema.fs index 79ae8ad13..e77413156 100644 --- a/tests/FSharp.Data.GraphQL.Benchmarks/Schema.fs +++ b/tests/FSharp.Data.GraphQL.Benchmarks/Schema.fs @@ -41,7 +41,7 @@ module SchemaDefinition = fieldsFn = fun () -> [ Define.Field("id", String, resolve = fun _ person -> person.Id) Define.Field("name", Nullable String, resolve = fun _ person -> person.Name) - Define.Field("friends", Nullable(ListOf(Nullable Person)), + Define.Field("friends", Nullable(SeqOf(Nullable Person)), resolve = fun _ person -> person.Friends |> List.map getPerson diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/CustomSchemaTypes.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/CustomSchemaTypes.fs deleted file mode 100644 index d500b74dd..000000000 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/CustomSchemaTypes.fs +++ /dev/null @@ -1,32 +0,0 @@ -namespace FSharp.Data.GraphQL.IntegrationTests.Server - -open System.IO -open FSharp.Data.GraphQL.Types -open FSharp.Data.GraphQL.Ast - -/// Represents a file in a GraphQL file upload. -type File = - /// Gets the name of the file. - { Name : string - /// Gets the type of the content of the file. - ContentType : string - /// Gets a stream which returns the content of the file when read. - Content : Stream } - -/// Contains customized schema definitions for extensibility features. -[] -module SchemaDefinitions = - let private coerceUploadInput (_ : Value) : File option = - failwith "Can not coerce upload input. The type `Upload` can only be passed as a variable through a multipart request." - - let private coerceUploadValue (value : obj) = - match value with - | :? File as file -> Some file - | _ -> None - - /// GraphQL type for binary data stream representation. - let Upload : ScalarDefinition = - { Name = "Upload" - Description = Some "The `Upload` type represents an upload of binary data." - CoerceInput = coerceUploadInput - CoerceValue = coerceUploadValue } \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/FSharp.Data.GraphQL.IntegrationTests.Server.fsproj b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/FSharp.Data.GraphQL.IntegrationTests.Server.fsproj index 76b64bfc3..becb2fe0c 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/FSharp.Data.GraphQL.IntegrationTests.Server.fsproj +++ b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/FSharp.Data.GraphQL.IntegrationTests.Server.fsproj @@ -7,19 +7,12 @@ + - - - - - - - - diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Helpers.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Helpers.fs deleted file mode 100644 index aff06beb8..000000000 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Helpers.fs +++ /dev/null @@ -1,53 +0,0 @@ -namespace FSharp.Data.GraphQL.IntegrationTests.Server - -open System -open System.Text -open Newtonsoft.Json -open Newtonsoft.Json.Linq -open Newtonsoft.Json.Serialization -open System.Collections.Generic -open FSharp.Data.GraphQL - -[] -module Helpers = - let tee f x = - f x - x - -[] -module StringHelpers = - let utf8String (bytes : byte seq) = - bytes - |> Seq.filter (fun i -> i > 0uy) - |> Array.ofSeq - |> Encoding.UTF8.GetString - - let utf8Bytes (str : string) = - str |> Encoding.UTF8.GetBytes - - let isNullOrWhiteSpace (str : string) = - String.IsNullOrWhiteSpace(str) - -[] -module JsonHelpers = - let tryGetJsonProperty (jobj: JObject) prop = - match jobj.Property(prop) with - | null -> None - | p -> Some(p.Value.ToString()) - - let getJsonSerializerSettings (converters : JsonConverter seq) = - JsonSerializerSettings() - |> tee (fun s -> - s.Converters <- List(converters) - s.ContractResolver <- CamelCasePropertyNamesContractResolver()) - - let getJsonSerializer (converters : JsonConverter seq) = - JsonSerializer() - |> tee (fun c -> - Seq.iter c.Converters.Add converters - c.ContractResolver <- CamelCasePropertyNamesContractResolver()) - - let private converters : JsonConverter [] = [| OptionConverter() |] - - let jsonSettings = getJsonSerializerSettings converters - let jsonSerializer = getJsonSerializer converters \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/HttpHandlers.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/HttpHandlers.fs deleted file mode 100644 index 99da1aeb2..000000000 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/HttpHandlers.fs +++ /dev/null @@ -1,165 +0,0 @@ -namespace FSharp.Data.GraphQL.IntegrationTests.Server - -open System.Text -open Giraffe -open Microsoft.AspNetCore.Http -open Newtonsoft.Json -open FSharp.Data.GraphQL.Execution -open System.IO -open FSharp.Data.GraphQL -open FSharp.Data.GraphQL.Types -open FSharp.Control.Tasks -open Newtonsoft.Json.Linq -open Giraffe.HttpStatusCodeHandlers.RequestErrors -open Microsoft.AspNetCore.WebUtilities -open FSharp.Data.GraphQL.Ast - -type HttpHandler = HttpFunc -> HttpContext -> HttpFuncResult - -module HttpHandlers = - let internalServerError : HttpHandler = setStatusCode 500 - - let okWithStr str : HttpHandler = setStatusCode 200 >=> text str - - let setCorsHeaders : HttpHandler = - setHttpHeader "Access-Control-Allow-Origin" "*" - >=> setHttpHeader "Access-Control-Allow-Headers" "content-type" - - let setContentTypeAsJson : HttpHandler = - setHttpHeader "Content-Type" "application/json" - - let isMultipartRequest (req : HttpRequest) = - not (System.String.IsNullOrEmpty(req.ContentType)) && req.ContentType.Contains("multipart/form-data") - - let getMultipartRequestBoundary (req : HttpRequest) = - req.Headers.GetCommaSeparatedValues("Content-Type") - |> Seq.map (fun v -> v.TrimStart()) - |> Seq.tryFind (fun v -> v.Contains("boundary")) - |> Option.map (fun v -> v.Remove(0, v.IndexOf('=') + 1)) - |> Option.map (fun v -> v.Trim('"')) - - let private graphQL (next : HttpFunc) (ctx : HttpContext) = task { - let serialize d = JsonConvert.SerializeObject(d, jsonSettings) - - let rec parseVariables (schema : ISchema) (defs : VariableDefinition list) (variables : obj) = - let casted = - match variables with - | null -> Map.empty - | :? string as x when System.String.IsNullOrWhiteSpace(x) -> Map.empty - | :? Map as x -> x - | :? JToken as x -> x.ToObject>(jsonSerializer) - | :? string as x -> JsonConvert.DeserializeObject>(x, jsonSettings) - | _ -> failwithf "Failure deserializing variables. Unexpected variables object format." - Variables.read schema defs casted - - let json = - function - | Direct (data, _) -> - JsonConvert.SerializeObject(data, jsonSettings) - | Deferred (data, _, deferred) -> - deferred |> Observable.add(fun d -> printfn "Deferred: %s" (serialize d)) - JsonConvert.SerializeObject(data, jsonSettings) - | Stream data -> - data |> Observable.add(fun d -> printfn "Subscription data: %s" (serialize d)) - "{}" - - let removeWhitespacesAndLineBreaks (str : string) = str.Trim().Replace("\r\n", " ") - - let readStream (s : Stream) = - use ms = new MemoryStream(4096) - s.CopyTo(ms) - ms.ToArray() - - let root = { RequestId = System.Guid.NewGuid().ToString() } - - let addRequestType (requestType : string) (response : GQLResponse) = - let mapper (content : GQLResponseContent) = - let dataMapper (data : Output) : Output = - let data = data |> Seq.map (|KeyValue|) |> Map.ofSeq - upcast data.Add("requestType", requestType) - match content with - | GQLResponseContent.Direct (data, errors) -> Direct (dataMapper data, errors) - | GQLResponseContent.Deferred (data, errors, deferred) -> Deferred (dataMapper data, errors, deferred) - | _ -> content - { Content = mapper response.Content; Metadata = response.Metadata } - - let parseVariableDefinitions (query : string) = - let ast = Parser.parse query - ast.Definitions - |> List.choose (function OperationDefinition def -> Some def.VariableDefinitions | _ -> None) - |> List.collect id - - let getVariables (vardefs : VariableDefinition list) (data : Map) = - match data.TryFind("variables") with - | Some null -> None - | Some variables -> parseVariables Schema.schema vardefs variables |> Some - | _ -> None - - if isMultipartRequest ctx.Request - then - let copyBodyToMemory (req : HttpRequest) = - let ms = new MemoryStream(4096) - req.Body.CopyTo(ms) - ms.Position <- 0L - ms - match getMultipartRequestBoundary ctx.Request with - | Some boundary -> - use ms = copyBodyToMemory(ctx.Request) - let reader = MultipartReader(boundary, ms) - let request = reader |> MultipartRequest.read |> Async.AwaitTask |> Async.RunSynchronously - let results = - request.Operations - |> List.map (fun op -> - let result = - match op.Variables with - | Some variables -> - let variables = parseVariables Schema.schema (parseVariableDefinitions op.Query) variables - Schema.executor.AsyncExecute(op.Query, variables = variables, data = root) - | None -> Schema.executor.AsyncExecute(op.Query, data = root) - result |> Async.RunSynchronously |> addRequestType "Multipart") - match results with - | [ result ] -> - return! okWithStr (json result) next ctx - | results -> - let result = JArray.FromObject(List.map json results).ToString() - return! okWithStr result next ctx - | None -> - return! badRequest (text "Invalid multipart request header: missing boundary value.") next ctx - else - let request = - let data = - let raw = Encoding.UTF8.GetString(readStream ctx.Request.Body) - if System.String.IsNullOrWhiteSpace(raw) - then None - else Some (JsonConvert.DeserializeObject>(raw, jsonSettings)) - data |> Option.bind (fun data -> - if data.ContainsKey("query") - then - match data.["query"] with - | :? string as query -> Some (query, getVariables (parseVariableDefinitions query) data) - | _ -> failwith "Failure deserializing repsonse. Could not read query - it is not stringified in request." - else None) - match request with - | Some (query, Some variables) -> - printfn "Received query: %s" query - printfn "Received variables: %A" variables - let query = removeWhitespacesAndLineBreaks query - let result = Schema.executor.AsyncExecute(query, root, variables) |> Async.RunSynchronously |> addRequestType "Classic" - printfn "Result metadata: %A" result.Metadata - return! okWithStr (json result) next ctx - | Some (query, None) -> - printfn "Received query: %s" query - let query = removeWhitespacesAndLineBreaks query - let result = Schema.executor.AsyncExecute(query) |> Async.RunSynchronously |> addRequestType "Classic" - printfn "Result metadata: %A" result.Metadata - return! okWithStr (json result) next ctx - | None -> - let result = Schema.executor.AsyncExecute(Introspection.IntrospectionQuery) |> Async.RunSynchronously |> addRequestType "Classic" - printfn "Result metadata: %A" result.Metadata - return! okWithStr (json result) next ctx - } - - let webApp : HttpHandler = - setCorsHeaders - >=> graphQL - >=> setContentTypeAsJson diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/JsonConverters.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/JsonConverters.fs deleted file mode 100644 index de5154b56..000000000 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/JsonConverters.fs +++ /dev/null @@ -1,31 +0,0 @@ -namespace FSharp.Data.GraphQL.IntegrationTests.Server - -open System.Collections.Generic -open Newtonsoft.Json -open Newtonsoft.Json.Linq -open Microsoft.FSharp.Reflection - -[] -type OptionConverter() = - inherit JsonConverter() - - override __.CanConvert(t) = - t.IsGenericType && t.GetGenericTypeDefinition() = typedefof> - - override __.WriteJson(writer, value, serializer) = - let value = - if isNull value then null - else - let _,fields = Microsoft.FSharp.Reflection.FSharpValue.GetUnionFields(value, value.GetType()) - fields.[0] - serializer.Serialize(writer, value) - - override __.ReadJson(reader, t, _, serializer) = - let innerType = t.GetGenericArguments().[0] - let innerType = - if innerType.IsValueType then (typedefof>).MakeGenericType([|innerType|]) - else innerType - let value = serializer.Deserialize(reader, innerType) - let cases = FSharpType.GetUnionCases(t) - if isNull value then FSharpValue.MakeUnion(cases.[0], [||]) - else FSharpValue.MakeUnion(cases.[1], [|value|]) \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/MultipartRequest.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/MultipartRequest.fs deleted file mode 100644 index a05d75bdc..000000000 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/MultipartRequest.fs +++ /dev/null @@ -1,159 +0,0 @@ -namespace FSharp.Data.GraphQL.IntegrationTests.Server - -open System -open Microsoft.AspNetCore.WebUtilities -open Newtonsoft.Json -open System.Collections -open System.Collections.Generic -open Newtonsoft.Json.Linq -open FSharp.Data.GraphQL.Ast -open FSharp.Data.GraphQL - -type GraphQLMultipartSection = - | Form of FormMultipartSection - | File of FileMultipartSection - static member FromSection(section : MultipartSection) = - match section with - | null -> None - | _ -> - match section.AsFormDataSection() with - | null -> - match section.AsFileSection() with - | null -> None - | x -> Some (File x) - | x -> Some (Form x) - member x.Name = - match x with - | Form x -> x.Name - | File x -> x.Name - -/// A GraphQL operation request. -type Operation = - /// Contains the query used in this operation. - { Query : string - /// Contains variables used by this operation. - Variables : Map option } - -/// A GrahpQL request using multipart request specification. -/// For more information, see https://github.com/jaydenseric/graphql-multipart-request-spec. -type MultipartRequest = - /// Contains the list of operations of this request. - /// If the request is not batched, then the single operation will be inside this list as a singleton. - { Operations : Operation list } - -/// Contains tools for working with GraphQL multipart requests. -module MultipartRequest = - let private parseOperations (operations: Operation list) (map : IDictionary) (files : IDictionary) = - let mapOperation (operationIndex : int option) (operation : Operation) = - let findFile (varName : string) (varValue : obj) = - let tryPickMultipleFilesFromMap (length : int) (varName : string) = - Seq.init length (fun ix -> - match map.TryGetValue(sprintf "%s.%i" varName ix) with - | (true, v) -> Some v - | _ -> None) - |> Seq.map (fun key -> - key |> Option.map (fun key -> - match files.TryGetValue(key) with - | (true, v) -> Some v - | _ -> None) - |> Option.flatten) - |> List.ofSeq - let pickMultipleFilesFromMap (length : int) (varName : string) = - Seq.init length (fun ix -> map.[sprintf "%s.%i" varName ix]) - |> Seq.map (fun key -> files.[key]) - |> List.ofSeq - let tryPickSingleFileFromMap varName = - let found = map |> Seq.choose (fun kvp -> if kvp.Key = varName then Some files.[kvp.Value] else None) |> List.ofSeq - match found with - | [x] -> Some x - | _ -> None - let pickSingleFileFromMap varName = - map - |> Seq.choose (fun kvp -> if kvp.Key = varName then Some files.[kvp.Value] else None) - |> Seq.exactlyOne - let pickFileRequestFromMap (request : UploadRequest) varName = - { UploadRequest.Single = pickSingleFileFromMap (sprintf "%s.single" varName) - Multiple = pickMultipleFilesFromMap request.Multiple.Length (sprintf "%s.multiple" varName) - NullableMultiple = request.NullableMultiple |> Option.map (fun x -> pickMultipleFilesFromMap x.Length (sprintf "%s.nullableMultiple" varName)) - NullableMultipleNullable = request.NullableMultipleNullable |> Option.map (fun x -> tryPickMultipleFilesFromMap x.Length (sprintf "%s.nullableMultipleNullable" varName)) } - let rec isUpload (t : InputType) = - match t with - | NamedType tname -> tname = "Upload" || tname = "UploadRequest" - | ListType t | NonNullType t -> isUpload t - let ast = Parser.parse operation.Query - let vardefs = - ast.Definitions - |> List.choose (function OperationDefinition def -> Some def.VariableDefinitions | _ -> None) - |> List.collect id - let vardef = vardefs |> List.find (fun x -> x.VariableName = varName) - if not (isUpload vardef.Type) - then varValue - else - match varValue with - | :? JObject as jreq -> - let request = jreq.ToObject(jsonSerializer) - let varName = - match operationIndex with - | Some operationIndex -> sprintf "%i.variables.%s" operationIndex varName - | None -> sprintf "variables.%s" varName - pickFileRequestFromMap request varName |> box - | :? IEnumerable as values -> - values - |> Seq.cast - |> Seq.mapi (fun valueIndex _ -> - let varName = - match operationIndex with - | Some operationIndex -> sprintf "%i.variables.%s.%i" operationIndex varName valueIndex - | None -> sprintf "variables.%s.%i" varName valueIndex - tryPickSingleFileFromMap varName |> Option.map box |> Option.toObj) - |> box - | _ -> - let varName = - match operationIndex with - | Some operationIndex -> sprintf "%i.variables.%s" operationIndex varName - | None -> sprintf "variables.%s" varName - tryPickSingleFileFromMap varName |> Option.map box |> Option.toObj - { operation with Variables = operation.Variables |> Option.map (Map.map (fun k v -> findFile k v)) } - match operations with - | [ operation ] -> [ mapOperation None operation ] - | operations -> operations |> List.mapi (fun ix operation -> mapOperation (Some ix) operation) - - /// Reads a GraphQL multipart request from a MultipartReader. - let read (reader : MultipartReader) = - async { - let mutable section : GraphQLMultipartSection option = None - let readNextSection () = - async { - let! next = reader.ReadNextSectionAsync() |> Async.AwaitTask - section <- GraphQLMultipartSection.FromSection(next) - } - let mutable operations : string = null - let mutable map : IDictionary = null - let files = Dictionary() - do! readNextSection () - while not section.IsNone do - match section.Value with - | Form section -> - let! value = section.GetValueAsync() |> Async.AwaitTask - match section.Name with - | "operations" -> - operations <- value - | "map" -> - map <- JsonConvert.DeserializeObject>(value) - |> Seq.map (fun kvp -> kvp.Value.Head, kvp.Key) - |> Map.ofSeq - | _ -> failwithf "Error reading multipart request. Unexpected section name \"%s\"." section.Name - | File section -> - let stream = new System.IO.MemoryStream(4096) - do! section.FileStream.CopyToAsync(stream) |> Async.AwaitTask - stream.Position <- 0L - let value = { Name = section.FileName; ContentType = section.Section.ContentType; Content = stream } - files.Add(section.Name, value) - do! readNextSection () - let operations = - match JToken.Parse(operations) with - | :? JArray as ops -> ops.ToObject(jsonSerializer) - | :? JObject as op -> [ op.ToObject(jsonSerializer) ] - | _ -> failwith "Unexpected operations value." - return { Operations = parseOperations operations map files } - } |> Async.StartAsTask \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Program.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Program.fs index 979d32584..a3e25a8ee 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Program.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Program.fs @@ -1,20 +1,51 @@ namespace FSharp.Data.GraphQL.IntegrationTests.Server + +open Microsoft.AspNetCore +open Microsoft.AspNetCore.Hosting +open System open Microsoft.AspNetCore +open Microsoft.AspNetCore.Http.Features +open Microsoft.AspNetCore.Builder open Microsoft.AspNetCore.Hosting +open Microsoft.Extensions.Logging +open Microsoft.Extensions.DependencyInjection +open FSharp.Data.GraphQL +open FSharp.Data.GraphQL.Server +open FSharp.Data.GraphQL.Server.AspNet module Program = - let exitCode = 0 + let configureApp (app : IApplicationBuilder) = + let schema = Schema(Schema.QueryType, Schema.MutationType) + let executor = Executor(schema) + let buildRoot ctx = async { return { RequestId = Guid.NewGuid().ToString() } } + app.UseGraphQL(executor, buildRoot, path="/") |> ignore - let [] BaseAddress = "localhost:8085" + let configureServices (services : IServiceCollection) = + services.AddDataProtection() |> ignore + services.Configure(fun (formOptions:FormOptions) -> + formOptions.MultipartBodyLengthLimit <- 2_147_483_648L + formOptions.ValueLengthLimit <- Int32.MaxValue + formOptions.BufferBodyLengthLimit <- 2147483648L + ) |> ignore - let buildWebHost args = - WebHost - .CreateDefaultBuilder(args) - .UseStartup() - .UseUrls(sprintf "http://%s" BaseAddress) + let configureLogging (loggerBuilder : ILoggingBuilder) = + loggerBuilder.AddFilter(fun lvl -> lvl.Equals LogLevel.Error) + .AddConsole() + .AddDebug() |> ignore [] - let main args = - buildWebHost(args).Build().Run() - exitCode + let main _ = + WebHost + .CreateDefaultBuilder() + .Configure(Action configureApp) + .ConfigureServices(configureServices) + .ConfigureLogging(configureLogging) + .UseKestrel(fun options -> + options.ListenAnyIP(8085) + options.Limits.MaxRequestBodySize <- Nullable 2147483648L + options.Limits.MaxRequestBufferSize <- Nullable 2147483648L + ) + .Build() + .Run() + 0 diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Schema.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Schema.fs index 8ffb2319a..4258529b3 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Schema.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Schema.fs @@ -25,10 +25,10 @@ type UploadedFile = ContentAsText : string } type UploadRequest = - { Single : File - Multiple : File list - NullableMultiple : File list option - NullableMultipleNullable : File option list option } + { Single : FileUpload + Multiple : FileUpload list + NullableMultiple : FileUpload list option + NullableMultipleNullable : FileUpload option list option } type UploadResponse = { Single : UploadedFile @@ -83,10 +83,10 @@ module Schema = name = "UploadRequest", description = "Request for uploading files in several different forms.", fields = - [ Define.Input("single", Upload, description = "A single file upload.") - Define.Input("multiple", ListOf Upload, description = "Multiple file uploads.") - Define.Input("nullableMultiple", Nullable (ListOf Upload), description = "Optional list of multiple file uploads.") - Define.Input("nullableMultipleNullable", Nullable (ListOf (Nullable Upload)), description = "Optional list of multiple optional file uploads.") ]) + [ Define.Input("single", FileUpload, description = "A single file upload.") + Define.Input("multiple", ListOf FileUpload, description = "Multiple file uploads.") + Define.Input("nullableMultiple", Nullable (ListOf FileUpload), description = "Optional list of multiple file uploads.") + Define.Input("nullableMultipleNullable", Nullable (ListOf (Nullable FileUpload)), description = "Optional list of multiple optional file uploads.") ]) let UploadResponseType = Define.Object( @@ -122,7 +122,7 @@ module Schema = let contentAsText (stream : System.IO.Stream) = use reader = new System.IO.StreamReader(stream, Encoding.UTF8) reader.ReadToEnd() - let mapUploadToOutput (file : File) = + let mapUploadToOutput (file : FileUpload) = { Name = file.Name; ContentType = file.ContentType; ContentAsText = contentAsText file.Content } let mapUploadRequestToOutput (request : UploadRequest) = { Single = mapUploadToOutput request.Single @@ -136,39 +136,35 @@ module Schema = name = "singleUpload", typedef = UploadedFileType, description = "Uploads a single file to the server and get it back.", - args = [ Define.Input("file", Upload, description = "The file to be uploaded.") ], + args = [ Define.Input("file", FileUpload, description = "The file to be uploaded.") ], resolve = fun ctx _ -> mapUploadToOutput (ctx.Arg("file"))) Define.Field( name = "nullableSingleUpload", typedef = Nullable UploadedFileType, description = "Uploads (maybe) a single file to the server and get it back (maybe).", - args = [ Define.Input("file", Nullable Upload, description = "The file to be uploaded.") ], + args = [ Define.Input("file", Nullable FileUpload, description = "The file to be uploaded.") ], resolve = fun ctx _ -> ctx.TryArg("file") |> Option.flatten |> Option.map mapUploadToOutput) Define.Field( name = "multipleUpload", - typedef = ListOf UploadedFileType, + typedef = SeqOf UploadedFileType, description = "Uploads a list of files to the server and get them back.", - args = [ Define.Input("files", ListOf Upload, description = "The files to upload.") ], + args = [ Define.Input("files", SeqOf FileUpload, description = "The files to upload.") ], resolve = fun ctx _ -> ctx.Arg("files") |> Seq.map mapUploadToOutput) Define.Field( name = "nullableMultipleUpload", - typedef = Nullable (ListOf UploadedFileType), + typedef = Nullable (SeqOf UploadedFileType), description = "Uploads (maybe) a list of files to the server and get them back (maybe).", - args = [ Define.Input("files", Nullable (ListOf Upload), description = "The files to upload.") ], + args = [ Define.Input("files", Nullable (SeqOf FileUpload), description = "The files to upload.") ], resolve = fun ctx _ -> ctx.TryArg("files") |> Option.flatten |> Option.map (Seq.map mapUploadToOutput)) Define.Field( name = "nullableMultipleNullableUpload", - typedef = Nullable (ListOf (Nullable UploadedFileType)), + typedef = Nullable (SeqOf (Nullable UploadedFileType)), description = "Uploads (maybe) a list of files (maybe) to the server and get them back (maybe).", - args = [ Define.Input("files", Nullable (ListOf (Nullable Upload)), description = "The files to upload.") ], + args = [ Define.Input("files", Nullable (SeqOf (Nullable FileUpload)), description = "The files to upload.") ], resolve = fun ctx _ -> ctx.TryArg("files") |> Option.flatten |> Option.map (Seq.map (Option.map mapUploadToOutput))) Define.Field( name = "uploadRequest", typedef = UploadResponseType, description = "Upload several files in different forms.", args = [ Define.Input("request", UploadRequestType, description = "The request for uploading several files in different forms.") ], - resolve = fun ctx _ -> mapUploadRequestToOutput (ctx.Arg("request"))) ]) - - let schema : ISchema = upcast Schema(QueryType, MutationType) - - let executor = Executor(schema) \ No newline at end of file + resolve = fun ctx _ -> mapUploadRequestToOutput (ctx.Arg("request"))) ]) \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Startup.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Startup.fs deleted file mode 100644 index 401872dc5..000000000 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Startup.fs +++ /dev/null @@ -1,30 +0,0 @@ -namespace FSharp.Data.GraphQL.IntegrationTests.Server - -open Microsoft.AspNetCore.Builder -open Microsoft.Extensions.Configuration -open Microsoft.Extensions.DependencyInjection -open Giraffe -open Microsoft.Extensions.Logging -open System -open Microsoft.AspNetCore.Server.Kestrel.Core - -type Startup private () = - new (configuration: IConfiguration) as this = - Startup() then - this.Configuration <- configuration - - member __.ConfigureServices(services: IServiceCollection) = - services.AddGiraffe() - .Configure(Action(fun x -> x.AllowSynchronousIO <- true)) - .Configure(Action(fun x -> x.AllowSynchronousIO <- true)) - |> ignore - - member __.Configure(app: IApplicationBuilder) = - let errorHandler (ex : Exception) (log : ILogger) = - log.LogError(EventId(), ex, "An unhandled exception has occurred while executing the request.") - clearResponse >=> setStatusCode 500 - app - .UseGiraffeErrorHandler(errorHandler) - .UseGiraffe HttpHandlers.webApp - - member val Configuration : IConfiguration = null with get, set diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Variables.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Variables.fs deleted file mode 100644 index 68e35b683..000000000 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Variables.fs +++ /dev/null @@ -1,51 +0,0 @@ -module FSharp.Data.GraphQL.IntegrationTests.Server.Variables - -open System -open FSharp.Data.GraphQL.Ast -open FSharp.Data.GraphQL.Types -open Newtonsoft.Json.Linq - -let private makeOption (t : Type) = typedefof<_ option>.MakeGenericType(t) -let private makeArray (t : Type) = t.MakeArrayType() -let private makeArrayOption = makeArray >> makeOption - -let read (schema : ISchema) (vardefs : VariableDefinition list) (variables : Map) = - let scalarTypes = - [| "Int", typeof - "Boolean", typeof - "Date", typeof - "Float", typeof - "ID", typeof - "String", typeof - "URI", typeof |] - |> Map.ofArray - let schemaTypes = - schema.TypeMap.ToSeq() - |> Seq.choose (fun (name, def) -> match def with | :? InputDef as idef -> Some (name, idef.Type) | _ -> None) - |> Map.ofSeq - let unwrapOption (t : Type) = - if t.IsGenericType && t.GetGenericTypeDefinition() = typedefof<_ option> - then t.GetGenericArguments().[0] - else failwithf "Expected type to be an Option type, but it is %s." t.Name - let rec resolveVariableType (inputType : InputType) = - match inputType with - | NamedType tname -> - match scalarTypes.TryFind tname with - | Some t -> makeOption t - | None -> - match schemaTypes.TryFind tname with - | Some t -> makeOption t - | None -> failwithf "Could not determine variable type \"%s\"." tname - | NonNullType t -> resolveVariableType t |> unwrapOption - | ListType t -> resolveVariableType t |> makeArrayOption - let resolveVariableValue (t : Type) (value : obj) = - match value with - | :? JToken as token -> token.ToObject(t, jsonSerializer) - | _ -> value - variables - |> Seq.map (|KeyValue|) - |> Seq.choose (fun (key, value) -> - vardefs - |> List.tryFind (fun def -> def.VariableName = key) - |> Option.map (fun def -> key, resolveVariableValue (resolveVariableType def.Type) value)) - |> Map.ofSeq \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests/Helpers.fs b/tests/FSharp.Data.GraphQL.IntegrationTests/Helpers.fs index 29ad5e082..48ce3d43f 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests/Helpers.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests/Helpers.fs @@ -25,10 +25,10 @@ type File = member x.MakeUpload() = let bytes = Encoding.UTF8.GetBytes(x.Content) new Upload(bytes, x.Name, x.ContentType) - static member FromDictionary(dict : IDictionary) = - { Name = downcast dict.["Name"] - ContentType = downcast dict.["ContentType"] - Content = downcast dict.["ContentAsText"] } + static member FromDictionary(dict : IReadOnlyDictionary) = + { Name = downcast dict.["name"] + ContentType = downcast dict.["contentType"] + Content = downcast dict.["contentAsText"] } type FilesRequest = { Single : File diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderTests.fs b/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderTests.fs index e6d5275e5..9f70bd820 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderTests.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderTests.fs @@ -5,7 +5,7 @@ open Helpers open FSharp.Data.GraphQL let [] ServerUrl = "http://localhost:8085" -let [] EmptyGuidAsString = "00000000-0000-0000-0000-000000000000" +let EmptyGuid = System.Guid.Empty type Provider = GraphQLProvider @@ -40,19 +40,17 @@ module SimpleOperation = type Operation = Provider.Operations.Q let validateResult (input : Input option) (result : Operation.OperationResult) = - result.CustomData.ContainsKey("requestType") |> equals true - result.CustomData.["requestType"] |> equals (box "Classic") result.Data.IsSome |> equals true input |> Option.iter (fun input -> result.Data.Value.Echo.IsSome |> equals true input.List |> Option.iter (fun list -> result.Data.Value.Echo.Value.List.IsSome |> equals true - let input = list |> Array.map (fun x -> x.Int, x.IntOption, x.String, x.StringOption, x.Uri, x.Guid.ToString()) + let input = list |> Array.map (fun x -> x.Int, x.IntOption, x.String, x.StringOption, x.Uri, x.Guid) let output = result.Data.Value.Echo.Value.List.Value |> Array.map (fun x -> x.Int, x.IntOption, x.String, x.StringOption, x.Uri, x.Guid) input |> equals output) input.Single |> Option.iter (fun single -> result.Data.Value.Echo.Value.Single.IsSome |> equals true - let input = single.Int, single.IntOption, single.String, single.StringOption, single.Uri, single.Guid.ToString() + let input = single.Int, single.IntOption, single.String, single.StringOption, single.Uri, single.Guid let output = result.Data.Value.Echo.Value.Single.Value |> map (fun x -> x.Int, x.IntOption, x.String, x.StringOption, x.Uri, x.Guid) input |> equals output)) @@ -106,21 +104,21 @@ let ``Should be able to execute a query using context, sending an empty input fi [] let ``Should be able to execute a query sending an input field with single field``() = - let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuidAsString) + let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuid) let input = Input(single) SimpleOperation.operation.Run(input) |> SimpleOperation.validateResult (Some input) [] let ``Should be able to execute a query using context, sending an an input field with single field``() = - let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuidAsString) + let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuid) let input = Input(single) SimpleOperation.operation.Run(context, input) |> SimpleOperation.validateResult (Some input) [] let ``Should be able to execute a query without sending an an input field with single field asynchronously``() = - let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuidAsString) + let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuid) let input = Input(single) SimpleOperation.operation.AsyncRun(input) |> Async.RunSynchronously @@ -128,7 +126,7 @@ let ``Should be able to execute a query without sending an an input field with s [] let ``Should be able to execute a query using context, sending an an input field with single field, asynchronously``() = - let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuidAsString) + let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuid) let input = Input(single) SimpleOperation.operation.AsyncRun(context, input) |> Async.RunSynchronously @@ -136,21 +134,21 @@ let ``Should be able to execute a query using context, sending an an input field [] let ``Should be able to execute a query sending an input field with list field``() = - let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuidAsString)|] + let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuid)|] let input = Input(list) SimpleOperation.operation.Run(input) |> SimpleOperation.validateResult (Some input) [] let ``Should be able to execute a query using context, sending an an input field with list field``() = - let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuidAsString)|] + let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuid)|] let input = Input(list) SimpleOperation.operation.Run(context, input) |> SimpleOperation.validateResult (Some input) [] let ``Should be able to execute a query without sending an an input field with list field asynchronously``() = - let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuidAsString)|] + let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuid)|] let input = Input(list) SimpleOperation.operation.AsyncRun(input) |> Async.RunSynchronously @@ -158,7 +156,7 @@ let ``Should be able to execute a query without sending an an input field with l [] let ``Should be able to execute a query using context, sending an an input field with list field, asynchronously``() = - let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuidAsString)|] + let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuid)|] let input = Input(list) SimpleOperation.operation.AsyncRun(context, input) |> Async.RunSynchronously @@ -166,24 +164,24 @@ let ``Should be able to execute a query using context, sending an an input field [] let ``Should be able to execute a query sending an input field with single and list fields``() = - let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuidAsString) - let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuidAsString)|] + let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuid) + let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuid)|] let input = Input(single, list) SimpleOperation.operation.Run(input) |> SimpleOperation.validateResult (Some input) [] let ``Should be able to execute a query using context, sending an an input field with single and list fields``() = - let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuidAsString) - let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuidAsString)|] + let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuid) + let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuid)|] let input = Input(single, list) SimpleOperation.operation.Run(context, input) |> SimpleOperation.validateResult (Some input) [] let ``Should be able to execute a query without sending an an input field with single and list fields asynchronously``() = - let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuidAsString) - let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuidAsString)|] + let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuid) + let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuid)|] let input = Input(single, list) SimpleOperation.operation.AsyncRun(input) |> Async.RunSynchronously @@ -191,8 +189,8 @@ let ``Should be able to execute a query without sending an an input field with s [] let ``Should be able to execute a query using context, sending an an input field with single and list fields, asynchronously``() = - let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuidAsString) - let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuidAsString)|] + let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuid) + let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuid)|] let input = Input(single, list) SimpleOperation.operation.AsyncRun(context, input) |> Async.RunSynchronously @@ -211,8 +209,6 @@ module SingleRequiredUploadOperation = type Operation = Provider.Operations.SingleUpload let validateResult (file : File) (result : Operation.OperationResult) = - result.CustomData.ContainsKey("requestType") |> equals true - result.CustomData.["requestType"] |> equals (box "Multipart") result.Data.IsSome |> equals true result.Data.Value.SingleUpload.Name |> equals file.Name result.Data.Value.SingleUpload.ContentAsText |> equals file.Content @@ -244,8 +240,6 @@ module SingleOptionalUploadOperation = type Operation = Provider.Operations.NullableSingleUpload let validateResult (file : File option) (result : Operation.OperationResult) = - result.CustomData.ContainsKey("requestType") |> equals true - result.CustomData.["requestType"] |> equals (box "Multipart") result.Data.IsSome |> equals true file |> Option.iter (fun file -> result.Data.Value.NullableSingleUpload.IsSome |> equals true @@ -290,8 +284,6 @@ module RequiredMultipleUploadOperation = type Operation = Provider.Operations.MultipleUpload let validateResult (files : File []) (result : Operation.OperationResult) = - result.CustomData.ContainsKey("requestType") |> equals true - result.CustomData.["requestType"] |> equals (box "Multipart") result.Data.IsSome |> equals true let receivedFiles = result.Data.Value.MultipleUpload @@ -328,8 +320,6 @@ module OptionalMultipleUploadOperation = type Operation = Provider.Operations.NullableMultipleUpload let validateResult (files : File [] option) (result : Operation.OperationResult) = - result.CustomData.ContainsKey("requestType") |> equals true - result.CustomData.["requestType"] |> equals (box "Multipart") result.Data.IsSome |> equals true let receivedFiles = result.Data.Value.NullableMultipleUpload @@ -377,8 +367,6 @@ module OptionalMultipleOptionalUploadOperation = type Operation = Provider.Operations.NullableMultipleNullableUpload let validateResult (files : File option [] option) (result : Operation.OperationResult) = - result.CustomData.ContainsKey("requestType") |> equals true - result.CustomData.["requestType"] |> equals (box "Multipart") result.Data.IsSome |> equals true let receivedFiles = result.Data.Value.NullableMultipleNullableUpload @@ -464,8 +452,6 @@ module UploadRequestOperation = type Request = Provider.Types.UploadRequest let validateResult (request : FilesRequest) (result : Operation.OperationResult) = - result.CustomData.ContainsKey("requestType") |> equals true - result.CustomData.["requestType"] |> equals (box "Multipart") result.Data.IsSome |> equals true result.Data.Value.UploadRequest.Single.ToDictionary() |> File.FromDictionary |> equals request.Single result.Data.Value.UploadRequest.Multiple |> Array.map ((fun x -> x.ToDictionary()) >> File.FromDictionary) |> equals request.Multiple diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderWithOptionalParametersOnlyTests.fs b/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderWithOptionalParametersOnlyTests.fs index 24fdebb96..bb12f023c 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderWithOptionalParametersOnlyTests.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderWithOptionalParametersOnlyTests.fs @@ -1,11 +1,12 @@ module FSharp.Data.GraphQL.IntegrationTests.LocalProviderWithOptionalParametersOnlyTests +open System open Xunit open Helpers open FSharp.Data.GraphQL let [] ServerUrl = "http://localhost:8085" -let [] EmptyGuidAsString = "00000000-0000-0000-0000-000000000000" +let EmptyGuid = Guid.Empty type Provider = GraphQLProvider @@ -40,19 +41,17 @@ module SimpleOperation = type Operation = Provider.Operations.Q let validateResult (input : Input option) (result : Operation.OperationResult) = - result.CustomData.ContainsKey("requestType") |> equals true - result.CustomData.["requestType"] |> equals (box "Classic") result.Data.IsSome |> equals true input |> Option.iter (fun input -> result.Data.Value.Echo.IsSome |> equals true input.List |> Option.iter (fun list -> result.Data.Value.Echo.Value.List.IsSome |> equals true - let input = list |> Array.map (fun x -> x.Int, x.IntOption, x.String, x.StringOption, x.Uri, x.Guid.ToString()) + let input = list |> Array.map (fun x -> x.Int, x.IntOption, x.String, x.StringOption, x.Uri, x.Guid) let output = result.Data.Value.Echo.Value.List.Value |> Array.map (fun x -> x.Int, x.IntOption, x.String, x.StringOption, x.Uri, x.Guid) input |> equals output) input.Single |> Option.iter (fun single -> result.Data.Value.Echo.Value.Single.IsSome |> equals true - let input = single.Int, single.IntOption, single.String, single.StringOption, single.Uri, single.Guid.ToString() + let input = single.Int, single.IntOption, single.String, single.StringOption, single.Uri, single.Guid let output = result.Data.Value.Echo.Value.Single.Value |> map (fun x -> x.Int, x.IntOption, x.String, x.StringOption, x.Uri, x.Guid) input |> equals output)) @@ -61,6 +60,7 @@ let ``Should be able to execute a query without sending input field``() = SimpleOperation.operation.Run() |> SimpleOperation.validateResult None + [] let ``Should be able to execute a query using context, without sending input field``() = SimpleOperation.operation.Run(context) @@ -106,21 +106,21 @@ let ``Should be able to execute a query using context, sending an empty input fi [] let ``Should be able to execute a query sending an input field with single field``() = - let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuidAsString) + let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuid) let input = Input(Some single) SimpleOperation.operation.Run(Some input) |> SimpleOperation.validateResult (Some input) [] let ``Should be able to execute a query using context, sending an an input field with single field``() = - let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuidAsString) + let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuid) let input = Input(Some single) SimpleOperation.operation.Run(context, Some input) |> SimpleOperation.validateResult (Some input) [] let ``Should be able to execute a query without sending an an input field with single field asynchronously``() = - let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuidAsString) + let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuid) let input = Input(Some single) SimpleOperation.operation.AsyncRun(Some input) |> Async.RunSynchronously @@ -128,7 +128,7 @@ let ``Should be able to execute a query without sending an an input field with s [] let ``Should be able to execute a query using context, sending an an input field with single field, asynchronously``() = - let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuidAsString) + let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuid) let input = Input(Some single) SimpleOperation.operation.AsyncRun(context, Some input) |> Async.RunSynchronously @@ -136,21 +136,21 @@ let ``Should be able to execute a query using context, sending an an input field [] let ``Should be able to execute a query sending an input field with list field``() = - let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuidAsString)|] + let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuid)|] let input = Input(list = Some list) SimpleOperation.operation.Run(Some input) |> SimpleOperation.validateResult (Some input) [] let ``Should be able to execute a query using context, sending an an input field with list field``() = - let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuidAsString)|] + let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuid)|] let input = Input(list = Some list) SimpleOperation.operation.Run(context, Some input) |> SimpleOperation.validateResult (Some input) [] let ``Should be able to execute a query without sending an an input field with list field asynchronously``() = - let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuidAsString)|] + let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuid)|] let input = Input(list = Some list) SimpleOperation.operation.AsyncRun(Some input) |> Async.RunSynchronously @@ -158,7 +158,7 @@ let ``Should be able to execute a query without sending an an input field with l [] let ``Should be able to execute a query using context, sending an an input field with list field, asynchronously``() = - let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuidAsString)|] + let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuid)|] let input = Input(list = Some list) SimpleOperation.operation.AsyncRun(context, Some input) |> Async.RunSynchronously @@ -166,24 +166,24 @@ let ``Should be able to execute a query using context, sending an an input field [] let ``Should be able to execute a query sending an input field with single and list fields``() = - let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuidAsString) - let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuidAsString)|] + let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuid) + let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuid)|] let input = Input(Some single, Some list) SimpleOperation.operation.Run(Some input) |> SimpleOperation.validateResult (Some input) [] let ``Should be able to execute a query using context, sending an an input field with single and list fields``() = - let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuidAsString) - let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuidAsString)|] + let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuid) + let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuid)|] let input = Input(Some single, Some list) SimpleOperation.operation.Run(context, Some input) |> SimpleOperation.validateResult (Some input) [] let ``Should be able to execute a query without sending an an input field with single and list fields asynchronously``() = - let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuidAsString) - let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuidAsString)|] + let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuid) + let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuid)|] let input = Input(Some single, Some list) SimpleOperation.operation.AsyncRun(Some input) |> Async.RunSynchronously @@ -191,13 +191,14 @@ let ``Should be able to execute a query without sending an an input field with s [] let ``Should be able to execute a query using context, sending an an input field with single and list fields, asynchronously``() = - let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuidAsString) - let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuidAsString)|] + let single = InputField("A", 2, System.Uri("http://localhost:1234"), EmptyGuid) + let list = [|InputField("A", 2, System.Uri("http://localhost:4321"), EmptyGuid)|] let input = Input(Some single, Some list) SimpleOperation.operation.AsyncRun(context, Some input) |> Async.RunSynchronously |> SimpleOperation.validateResult (Some input) + module SingleRequiredUploadOperation = let operation = Provider.Operation<"""mutation SingleUpload($file: Upload!) { @@ -211,8 +212,6 @@ module SingleRequiredUploadOperation = type Operation = Provider.Operations.SingleUpload let validateResult (file : File) (result : Operation.OperationResult) = - result.CustomData.ContainsKey("requestType") |> equals true - result.CustomData.["requestType"] |> equals (box "Multipart") result.Data.IsSome |> equals true result.Data.Value.SingleUpload.Name |> equals file.Name result.Data.Value.SingleUpload.ContentAsText |> equals file.Content @@ -244,8 +243,6 @@ module SingleOptionalUploadOperation = type Operation = Provider.Operations.NullableSingleUpload let validateResult (file : File option) (result : Operation.OperationResult) = - result.CustomData.ContainsKey("requestType") |> equals true - result.CustomData.["requestType"] |> equals (box "Multipart") result.Data.IsSome |> equals true file |> Option.iter (fun file -> result.Data.Value.NullableSingleUpload.IsSome |> equals true @@ -290,8 +287,6 @@ module RequiredMultipleUploadOperation = type Operation = Provider.Operations.MultipleUpload let validateResult (files : File []) (result : Operation.OperationResult) = - result.CustomData.ContainsKey("requestType") |> equals true - result.CustomData.["requestType"] |> equals (box "Multipart") result.Data.IsSome |> equals true let receivedFiles = result.Data.Value.MultipleUpload @@ -328,8 +323,6 @@ module OptionalMultipleUploadOperation = type Operation = Provider.Operations.NullableMultipleUpload let validateResult (files : File [] option) (result : Operation.OperationResult) = - result.CustomData.ContainsKey("requestType") |> equals true - result.CustomData.["requestType"] |> equals (box "Multipart") result.Data.IsSome |> equals true let receivedFiles = result.Data.Value.NullableMultipleUpload @@ -377,8 +370,6 @@ module OptionalMultipleOptionalUploadOperation = type Operation = Provider.Operations.NullableMultipleNullableUpload let validateResult (files : File option [] option) (result : Operation.OperationResult) = - result.CustomData.ContainsKey("requestType") |> equals true - result.CustomData.["requestType"] |> equals (box "Multipart") result.Data.IsSome |> equals true let receivedFiles = result.Data.Value.NullableMultipleNullableUpload @@ -464,8 +455,6 @@ module UploadRequestOperation = type Request = Provider.Types.UploadRequest let validateResult (request : FilesRequest) (result : Operation.OperationResult) = - result.CustomData.ContainsKey("requestType") |> equals true - result.CustomData.["requestType"] |> equals (box "Multipart") result.Data.IsSome |> equals true result.Data.Value.UploadRequest.Single.ToDictionary() |> File.FromDictionary |> equals request.Single result.Data.Value.UploadRequest.Multiple |> Array.map ((fun x -> x.ToDictionary()) >> File.FromDictionary) |> equals request.Multiple @@ -489,4 +478,4 @@ let ``Should be able to upload files inside another input type``() = nullableMultiple = Some (Array.map makeUpload request.NullableMultiple.Value), nullableMultipleNullable = Some (Array.map (Option.map makeUpload) request.NullableMultipleNullable.Value)) UploadRequestOperation.operation.Run(input) - |> UploadRequestOperation.validateResult request \ No newline at end of file + |> UploadRequestOperation.validateResult request diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests/SwapiLocalProviderTests.fs b/tests/FSharp.Data.GraphQL.IntegrationTests/SwapiLocalProviderTests.fs index 3f5a154cd..07b331918 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests/SwapiLocalProviderTests.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests/SwapiLocalProviderTests.fs @@ -51,21 +51,6 @@ module SimpleOperation = Some (upcast Operation.Types.HeroFields.FriendsFields.Droid(name = "R2-D2", primaryFunction = "Astromech")) |] result.Data.Value.Hero.Value.Friends |> equals expectedFriends result.Data.Value.Hero.Value.HomePlanet |> equals (Some "Tatooine") - let actual = normalize <| sprintf "%A" result.Data - let expected = normalize <| """Some - {Hero = Some - {AppearsIn = [|NewHope; Empire; Jedi|]; - Friends = [|Some {HomePlanet = ; - Name = Some "Han Solo";}; - Some {HomePlanet = Some "Alderaan"; - Name = Some "Leia Organa";}; - Some {Name = Some "C-3PO"; - PrimaryFunction = Some "Protocol";}; - Some {Name = Some "R2-D2"; - PrimaryFunction = Some "Astromech";}|]; - HomePlanet = Some "Tatooine"; - Name = Some "Luke Skywalker";};}""" - actual |> equals expected [] let ``Should be able to start a simple query operation synchronously`` () = @@ -168,21 +153,6 @@ module FileOperation = Some (upcast Operation.Types.HeroFields.FriendsFields.Droid(name = "R2-D2", primaryFunction = "Astromech")) |] result.Data.Value.Hero.Value.Friends |> equals expectedFriends result.Data.Value.Hero.Value.HomePlanet |> equals (Some "Tatooine") - let actual = normalize <| sprintf "%A" result.Data - let expected = normalize <| """Some - {Hero = Some - {AppearsIn = [|NewHope; Empire; Jedi|]; - Friends = [|Some {HomePlanet = ; - Name = Some "Han Solo";}; - Some {HomePlanet = Some "Alderaan"; - Name = Some "Leia Organa";}; - Some {Name = Some "C-3PO"; - PrimaryFunction = Some "Protocol";}; - Some {Name = Some "R2-D2"; - PrimaryFunction = Some "Astromech";}|]; - HomePlanet = Some "Tatooine"; - Name = Some "Luke Skywalker";};}""" - actual |> equals expected [] let ``Should be able to run a query from a query file`` () = diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests/SwapiRemoteProviderTests.fs b/tests/FSharp.Data.GraphQL.IntegrationTests/SwapiRemoteProviderTests.fs index fbcadc495..d66f60b6f 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests/SwapiRemoteProviderTests.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests/SwapiRemoteProviderTests.fs @@ -43,21 +43,6 @@ module SimpleOperation = Some (upcast Operation.Types.HeroFields.FriendsFields.Droid(name = "R2-D2", primaryFunction = "Astromech")) |] result.Data.Value.Hero.Value.Friends |> equals expectedFriends result.Data.Value.Hero.Value.HomePlanet |> equals (Some "Tatooine") - let actual = normalize <| sprintf "%A" result.Data - let expected = normalize <| """Some - {Hero = Some - {AppearsIn = [|NewHope; Empire; Jedi|]; - Friends = [|Some {HomePlanet = ; - Name = Some "Han Solo";}; - Some {HomePlanet = Some "Alderaan"; - Name = Some "Leia Organa";}; - Some {Name = Some "C-3PO"; - PrimaryFunction = Some "Protocol";}; - Some {Name = Some "R2-D2"; - PrimaryFunction = Some "Astromech";}|]; - HomePlanet = Some "Tatooine"; - Name = Some "Luke Skywalker";};}""" - actual |> equals expected [] let ``Should be able to start a simple query operation synchronously`` () = @@ -156,21 +141,6 @@ module FileOperation = Some (upcast Operation.Types.HeroFields.FriendsFields.Droid(name = "R2-D2", primaryFunction = "Astromech")) |] result.Data.Value.Hero.Value.Friends |> equals expectedFriends result.Data.Value.Hero.Value.HomePlanet |> equals (Some "Tatooine") - let actual = normalize <| sprintf "%A" result.Data - let expected = normalize <| """Some - {Hero = Some - {AppearsIn = [|NewHope; Empire; Jedi|]; - Friends = [|Some {HomePlanet = ; - Name = Some "Han Solo";}; - Some {HomePlanet = Some "Alderaan"; - Name = Some "Leia Organa";}; - Some {Name = Some "C-3PO"; - PrimaryFunction = Some "Protocol";}; - Some {Name = Some "R2-D2"; - PrimaryFunction = Some "Astromech";}|]; - HomePlanet = Some "Tatooine"; - Name = Some "Luke Skywalker";};}""" - actual |> equals expected [] let ``Should be able to run a query from a query file`` () = diff --git a/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs b/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs index ea40b9972..bfa927019 100644 --- a/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/MiddlewareTests.fs @@ -58,7 +58,7 @@ let executor = fieldsFn = fun () -> [ Define.Field("id", Int, resolve = fun _ a -> a.id) Define.Field("value", String, resolve = fun _ a -> a.value) - Define.Field("subjects", Nullable (ListOf (Nullable SubjectType)), + Define.Field("subjects", Nullable (SeqOf (Nullable SubjectType)), resolve = fun _ (a : A) -> a.subjects |> List.map getSubject |> List.toSeq |> Some) .WithQueryWeight(1.0) ]) and BType = @@ -68,7 +68,7 @@ let executor = fieldsFn = fun () -> [ Define.Field("id", Int, resolve = fun _ b -> b.id) Define.Field("value", String, resolve = fun _ b -> b.value) - Define.Field("subjects", Nullable (ListOf (Nullable SubjectType)), + Define.Field("subjects", Nullable (SeqOf (Nullable SubjectType)), resolve = fun _ (b : B) -> b.subjects |> List.map getSubject |> List.toSeq |> Some) .WithQueryWeight(1.0) ]) let Query = diff --git a/tests/FSharp.Data.GraphQL.Tests/VariablesTests.fs b/tests/FSharp.Data.GraphQL.Tests/VariablesTests.fs index cbaaa8496..91aa669f6 100644 --- a/tests/FSharp.Data.GraphQL.Tests/VariablesTests.fs +++ b/tests/FSharp.Data.GraphQL.Tests/VariablesTests.fs @@ -21,7 +21,7 @@ let TestComplexScalar = type TestInput = { a: string option - b: string option seq option + b: string option list option c: string d: string option } @@ -74,10 +74,10 @@ let TestType = Define.Field("fieldWithDefaultArgumentValue", String, "", [ Define.Input("input", Nullable String, Some "hello world") ], stringifyInput) Define.Field("fieldWithNestedInputObject", String, "", [ Define.Input("input", TestNestedInputObject, { na = None; nb = "hello world"}) ], stringifyInput) Define.Field("fieldWithEnumInput", String, "", [ Define.Input("input", EnumTestType) ], stringifyInput) - Define.Field("list", String, "", [ Define.Input("input", Nullable(ListOf (Nullable String))) ], stringifyInput) + Define.Field("list", String, "", [ Define.Input("input", Nullable(SeqOf (Nullable String))) ], stringifyInput) Define.Field("nnList", String, "", [ Define.Input("input", ListOf (Nullable String)) ], stringifyInput) - Define.Field("listNN", String, "", [ Define.Input("input", Nullable (ListOf String)) ], stringifyInput) - Define.Field("nnListNN", String, "", [ Define.Input("input", ListOf String) ], stringifyInput) + Define.Field("listNN", String, "", [ Define.Input("input", Nullable (SeqOf String)) ], stringifyInput) + Define.Field("nnListNN", String, "", [ Define.Input("input", SeqOf String) ], stringifyInput) ]) let schema = Schema(TestType) @@ -132,7 +132,7 @@ let ``Execute handles variables with complex inputs`` () = fieldWithObjectInput(input: $input) }""" let params' : Map = - Map.ofList ["input", upcast { a = Some "foo"; b = Some (upcast [ Some "bar"]) ; c = "baz"; d = None }] + Map.ofList ["input", upcast { a = Some "foo"; b = Some ([ Some "bar"]) ; c = "baz"; d = None }] let actual = sync <| Executor(schema).AsyncExecute(ast, variables = params') let expected = NameValueLookup.ofList [ "fieldWithObjectInput", upcast """{"a":"foo","b":["bar"],"c":"baz","d":null}""" ] match actual with