From 578162d5124a50f90fc354666dc4d3c8db93de72 Mon Sep 17 00:00:00 2001 From: John Berzy Date: Sun, 18 Apr 2021 22:50:20 -0700 Subject: [PATCH 1/8] first take at middleware --- FSharp.Data.GraphQL.sln | 15 ++ paket.dependencies | 12 +- ...rp.Data.GraphQL.Samples.StarWarsApi.fsproj | 6 +- samples/star-wars-api/Helpers.fs | 47 ------ samples/star-wars-api/HttpHandlers.fs | 112 ------------- samples/star-wars-api/JsonConverters.fs | 149 ----------------- samples/star-wars-api/WebSocketMessages.fs | 22 --- samples/star-wars-api/WebSocketMiddleware.fs | 157 ------------------ 8 files changed, 27 insertions(+), 493 deletions(-) delete mode 100644 samples/star-wars-api/Helpers.fs delete mode 100644 samples/star-wars-api/HttpHandlers.fs delete mode 100644 samples/star-wars-api/JsonConverters.fs delete mode 100644 samples/star-wars-api/WebSocketMessages.fs delete mode 100644 samples/star-wars-api/WebSocketMiddleware.fs diff --git a/FSharp.Data.GraphQL.sln b/FSharp.Data.GraphQL.sln index f08c5fa9d..cefc8458a 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.AspNet", "src\FSharp.Data.GraphQL.AspNet\FSharp.Data.GraphQL.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/paket.dependencies b/paket.dependencies index 092d9ae36..f5169cae0 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -38,4 +38,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/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj b/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj index 87cffddde..21d40e186 100644 --- a/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj +++ b/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj @@ -8,14 +8,10 @@ + - - - - - 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/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 From c48cac20f7651eea169874edf4a97cfb08d94f04 Mon Sep 17 00:00:00 2001 From: John Berzy Date: Sun, 18 Apr 2021 22:50:38 -0700 Subject: [PATCH 2/8] first take at middleware --- .../FSharp.Data.GraphQL.AspNet.fsproj | 14 + src/FSharp.Data.GraphQL.AspNet/GraphQL.fs | 519 ++++++++++++++++++ .../paket.references | 8 + 3 files changed, 541 insertions(+) create mode 100644 src/FSharp.Data.GraphQL.AspNet/FSharp.Data.GraphQL.AspNet.fsproj create mode 100644 src/FSharp.Data.GraphQL.AspNet/GraphQL.fs create mode 100644 src/FSharp.Data.GraphQL.AspNet/paket.references diff --git a/src/FSharp.Data.GraphQL.AspNet/FSharp.Data.GraphQL.AspNet.fsproj b/src/FSharp.Data.GraphQL.AspNet/FSharp.Data.GraphQL.AspNet.fsproj new file mode 100644 index 000000000..9edb021cb --- /dev/null +++ b/src/FSharp.Data.GraphQL.AspNet/FSharp.Data.GraphQL.AspNet.fsproj @@ -0,0 +1,14 @@ + + + netstandard2.0 + preview + + + + + + + + + + \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.AspNet/GraphQL.fs b/src/FSharp.Data.GraphQL.AspNet/GraphQL.fs new file mode 100644 index 000000000..b71be0668 --- /dev/null +++ b/src/FSharp.Data.GraphQL.AspNet/GraphQL.fs @@ -0,0 +1,519 @@ +namespace FSharp.Data.GraphQL.AspNet + +open System +open System.IO +open System.Net +open System.Reflection +open System.Text.Json +open System.Text.Json.Serialization +open System.Threading.Tasks +open System.Security.Cryptography +open System.Collections.Concurrent + +open FSharp.Reflection +open FSharp.Data.GraphQL +open FSharp.Data.GraphQL.Execution +open FSharp.Data.GraphQL.Types +open FSharp.Data.GraphQL.Types.Patterns + +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 +open Microsoft.AspNetCore.Http.Features + +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 + FileType : string + Size : int + Content : Stream + Path : string + Hash : string } + +[] +module Constants = + let [] QueryJsonKey = "query" + let [] VariablesJsonKey = "variables" + let [] DataJsonKey = "data" + +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(DataJsonKey) + +type IJsonVariableReader = + abstract IsNullable : bool + abstract Read : replacements: Map * path: string * element: JsonElement -> obj + +type TypeReaderCache = ConcurrentDictionary + + +[] +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 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.") + ) + + +module private JsonReader = + + 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 caseInfos = FSharpType.GetUnionCases(optionType.MakeGenericType(optionInnerType)) + let noneCtor = caseInfos |> Seq.find(fun case -> case.Name = "None") |> FSharpValue.PreComputeUnionConstructor + let someCtor = caseInfos |> Seq.find(fun case -> case.Name = "Some") |> FSharpValue.PreComputeUnionConstructor + someCtor, noneCtor + + let private getCallInfo = function + | Quotations.Patterns.Call(_, info, _) -> info + | _ -> failwith "Unexpected Quotation!" + + 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: IJsonVariableReader = getJsonReader innerDef cache + let someCtor, noneCtor = getOptionCtors innerDef.Type + fun replacements path (element: JsonElement) -> + match element.ValueKind with + | JsonValueKind.Null -> 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: IJsonVariableReader = getJsonReader elementDef cache + let genericType = listDef.Type.GetGenericTypeDefinition() + let collectionConverterMethInfo = + if genericType = typedefof> then getCallInfo <@ List.ofSeq<_> Seq.empty @> + elif genericType = typedefof> then getCallInfo <@ Array.ofSeq<_> Seq.empty @> + elif genericType = typedefof> then getCallInfo <@ Set.ofSeq<_> Seq.empty @> + else getCallInfo <@ Seq.cast<_> 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()) + match Map.tryFind elementPath replacements with + | Some value -> box value + | None -> elementReader.Read(replacements, elementPath, value) ) + |> Array.ofSeq + |> 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, (field, toCamelCase field.Name, getJsonReader field.TypeDef cache)) + |> Map.ofArray + let fieldReaders = + [ for field in FSharpType.GetRecordFields(inputObject.Type, true) do + match Map.tryFind field.Name 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 readVariables (replacements: Map) (index: int option) (variablesJson: JsonElement option) (vars: list) = + let readerCache = ConcurrentDictionary() + let basePath = + match index with + | Some idx -> combinePath (idx.ToString()) VariablesJsonKey + | None -> VariablesJsonKey + let tryReadProperty = + match variablesJson with + | Some variables -> + fun (name: string) -> + match variables.TryGetProperty(name) with + | true, value -> Some value + | false, _ -> None + | None -> + fun (name: string) -> None + Map.ofList [ + for var in vars do + match tryReadProperty var.Name with + | Some element -> + let path = combinePath basePath var.Name + let reader = getJsonReader var.TypeDef readerCache + let variableValue = reader.Read(replacements, path, element) + (var.Name, variableValue) + | None -> + match var.DefaultValue, var.TypeDef with + | Some _, _ -> () + | _, Nullable _ -> () + | _ -> failwithf "Variable '%s' is missing and there is no default value" var.Name + ] + +module private ExecutionHandler = + + 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 flags = FileOptions.Asynchronous ||| FileOptions.SequentialScan + let stream = new FileStream(filepath, FileMode.Create, FileAccess.Write, FileShare.None, 4096, flags) + use hash = new CryptoStream(stream, cryptoHash, CryptoStreamMode.Write) + do! section.Body.CopyToAsync(hash) |> Async.AwaitTask + hash.FlushFinalBlock() + stream.Position <- 0L + let file = + { Name = fileName; FileType = 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 rec readMappingAux (reader: byref) (files: Map) (accum: (string * FileUpload) list) = + match reader.TokenType with + | JsonTokenType.String -> + let mappedFile = reader.GetString() + let file = files.[mappedFile] + match reader.TokenType with + | JsonTokenType.StartArray -> + reader.Read() |> ignore + let path = reader.GetString() + reader.Read() |> ignore + readMappingAux &reader files ((path, file)::accum) + | invalidToken -> + let message = sprintf "Expected 'BeginArray' but received json token '%A'" invalidToken + raise <| invalidOp message + | JsonTokenType.EndObject -> + Map.ofSeq accum + | invalidToken -> + let message = sprintf "Expected PropertyName or EndObject for property mapping but received json token '%A'" invalidToken + raise <| invalidOp message + + let readMapping (json: string) (fileMapping: Map) = + let bytes = System.Text.Encoding.UTF8.GetBytes(json) + let readonlySpan = ReadOnlySpan(bytes) + let mutable reader = Utf8JsonReader(readonlySpan) + match reader.TokenType with + | JsonTokenType.StartObject -> + reader.Read() |> ignore + readMappingAux &reader fileMapping [] + | invalidToken -> + let message = sprintf "Expected PropertyName or EndObject for property mapping but received json token '%A'" invalidToken + raise <| invalidOp message + + + + let readOperation (executor: Executor<'T>) (element: JsonElement) (replacements: Map) (index: int option) = + match element.ValueKind with + | JsonValueKind.Object -> + let queryText = + match element.TryGetProperty(QueryJsonKey) with + | true, queryElement -> queryElement.GetString() + | false, _ -> Introspection.IntrospectionQuery + + let variablesElement = + match element.TryGetProperty(VariablesJsonKey) with + | true, value -> Some value + | false, value -> None + + let plan = executor.CreateExecutionPlan(queryText) + let variables = JsonReader.readVariables 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 (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 executor operation replacements (Some i)) + |> Seq.toList + Batch operations + | _ -> + let operation = readOperation 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 (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 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 (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 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>) = + let graphQLSerializationOptions = JsonSerializerOptions() + graphQLSerializationOptions.Converters.Add(GQLResponseConverter()) + graphQLSerializationOptions.Converters.Add(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 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 graphQLHandler = ExecutionHandler.queryHandler buildArguments executor + builder.Map(PathString urlPath, (fun (builder:IApplicationBuilder) -> + builder.Run(RequestDelegate(graphQLHandler)))) \ No newline at end of file diff --git a/src/FSharp.Data.GraphQL.AspNet/paket.references b/src/FSharp.Data.GraphQL.AspNet/paket.references new file mode 100644 index 000000000..6180f0615 --- /dev/null +++ b/src/FSharp.Data.GraphQL.AspNet/paket.references @@ -0,0 +1,8 @@ +nuget 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 From a7dcfa2131d4ce4891c6b8dbb7b57d1cb3c06492 Mon Sep 17 00:00:00 2001 From: John Berzy Date: Wed, 21 Apr 2021 19:35:08 -0700 Subject: [PATCH 3/8] wip --- paket.lock | 65 +++++++ .../file-uploads/ApplicationInsights.config | 0 ...arp.Data.GraphQL.Samples.FileUpload.fsproj | 20 +++ samples/file-uploads/Program.fs | 50 ++++++ .../Properties/launchSettings.json | 27 +++ samples/file-uploads/Schema.fs | 66 +++++++ samples/file-uploads/appsettings.json | 8 + samples/file-uploads/paket.references | 2 + ...rp.Data.GraphQL.Samples.StarWarsApi.fsproj | 1 - samples/star-wars-api/Program.fs | 50 ++++-- samples/star-wars-api/Schema.fs | 13 +- samples/star-wars-api/Startup.fs | 32 ---- .../AssemblyInfo.fs | 17 ++ .../FSharp.Data.GraphQL.AspNet.fsproj | 1 + src/FSharp.Data.GraphQL.AspNet/GraphQL.fs | 168 +++++++++--------- src/FSharp.Data.GraphQL.AspNet/Types.fs | 25 +++ .../paket.references | 2 +- .../Relay/Connections.fs | 2 +- .../Introspection.fs | 20 +-- src/FSharp.Data.GraphQL.Shared/TypeSystem.fs | 15 +- .../AsyncSchema.fs | 2 +- .../FSharp.Data.GraphQL.Benchmarks/Schema.fs | 2 +- .../CustomSchemaTypes.fs | 32 ---- ...ata.GraphQL.IntegrationTests.Server.fsproj | 8 +- .../Helpers.fs | 53 ------ .../HttpHandlers.fs | 165 ----------------- .../JsonConverters.fs | 31 ---- .../MultipartRequest.fs | 159 ----------------- .../Program.fs | 51 ++++-- .../Schema.fs | 40 ++--- .../Startup.fs | 30 ---- .../Variables.fs | 51 ------ .../LocalProviderTests.fs | 7 +- ...ProviderWithOptionalParametersOnlyTests.fs | 18 +- .../foo.txt | 1 + .../script.fsx | 67 +++++++ .../MiddlewareTests.fs | 4 +- .../VariablesTests.fs | 8 +- 38 files changed, 579 insertions(+), 734 deletions(-) create mode 100644 samples/file-uploads/ApplicationInsights.config create mode 100644 samples/file-uploads/FSharp.Data.GraphQL.Samples.FileUpload.fsproj create mode 100644 samples/file-uploads/Program.fs create mode 100644 samples/file-uploads/Properties/launchSettings.json create mode 100644 samples/file-uploads/Schema.fs create mode 100644 samples/file-uploads/appsettings.json create mode 100644 samples/file-uploads/paket.references delete mode 100644 samples/star-wars-api/Startup.fs create mode 100644 src/FSharp.Data.GraphQL.AspNet/AssemblyInfo.fs create mode 100644 src/FSharp.Data.GraphQL.AspNet/Types.fs delete mode 100644 tests/FSharp.Data.GraphQL.IntegrationTests.Server/CustomSchemaTypes.fs delete mode 100644 tests/FSharp.Data.GraphQL.IntegrationTests.Server/Helpers.fs delete mode 100644 tests/FSharp.Data.GraphQL.IntegrationTests.Server/HttpHandlers.fs delete mode 100644 tests/FSharp.Data.GraphQL.IntegrationTests.Server/JsonConverters.fs delete mode 100644 tests/FSharp.Data.GraphQL.IntegrationTests.Server/MultipartRequest.fs delete mode 100644 tests/FSharp.Data.GraphQL.IntegrationTests.Server/Startup.fs delete mode 100644 tests/FSharp.Data.GraphQL.IntegrationTests.Server/Variables.fs create mode 100644 tests/FSharp.Data.GraphQL.IntegrationTests/foo.txt create mode 100644 tests/FSharp.Data.GraphQL.IntegrationTests/script.fsx diff --git a/paket.lock b/paket.lock index 86700f6ee..238575821 100644 --- a/paket.lock +++ b/paket.lock @@ -2,6 +2,71 @@ NUGET remote: https://api.nuget.org/v3/index.json NuGet.CommandLine (5.8.1) +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.5) + 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.5) + 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) NUGET diff --git a/samples/file-uploads/ApplicationInsights.config b/samples/file-uploads/ApplicationInsights.config new file mode 100644 index 000000000..e69de29bb 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..a5fb8e0ce --- /dev/null +++ b/samples/file-uploads/FSharp.Data.GraphQL.Samples.FileUpload.fsproj @@ -0,0 +1,20 @@ + + + Exe + net5.0 + false + + + + + + + + + + + + + + + diff --git a/samples/file-uploads/Program.fs b/samples/file-uploads/Program.fs new file mode 100644 index 000000000..cc162e915 --- /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.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..8fcbdc3d1 --- /dev/null +++ b/samples/file-uploads/Schema.fs @@ -0,0 +1,66 @@ +namespace FSharp.Data.GraphQL.Samples.FileUpload + +open FSharp.Data.GraphQL.Types +open type FSharp.Data.GraphQL.Types.SchemaDefinitions.Define +open FSharp.Data.GraphQL.AspNet + +type Uploads = + { Files : FSharp.Data.GraphQL.AspNet.FileUpload list option } + +type Root = unit + +module Schema = + let UploadType = + 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 an AST input.") + ) + + let UploadsType = + InputObject( + name = "Uploads", + description = "The `Upload` scalar type represents a file upload.", + fields = [ + Input("files", Nullable(ListOf UploadType)) + ] + ) + + let Query = + Object( + name = "Query", + fields = [ + Field("Message", String, "A list of the employees", fun ctx _ -> "Hello World") + ] + ) + + + let Mutation = + Object( + name = "Mutation", + fields = [ + AsyncField("singleUpload", Boolean, "upload a single file", [Input("file", UploadType)], + fun ctx _ -> async { + let file = ctx.Arg("file") + printfn $"File: {file}" + return true + }) + AsyncField("multiUpload", Boolean, "upload a single file", [Input("files", ListOf UploadType)], + fun ctx _ -> async { + let files = ctx.Arg("files") + printfn $"Files: {files}" + return true + }) + AsyncField("multiUploadRecord", Boolean, "upload a single file", [Input("files", UploadsType)], + fun ctx _ -> async { + let files = ctx.Arg("files") + printfn $"Files: {files}" + return true + }) + ] + ) \ 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/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 21d40e186..a5fb8e0ce 100644 --- a/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj +++ b/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj @@ -12,7 +12,6 @@ - diff --git a/samples/star-wars-api/Program.fs b/samples/star-wars-api/Program.fs index cd92f94c4..565ccfbdd 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.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..6f06462a3 100644 --- a/samples/star-wars-api/Schema.fs +++ b/samples/star-wars-api/Schema.fs @@ -142,7 +142,7 @@ module Schema = [ 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.", + Define.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) @@ -157,7 +157,7 @@ module Schema = [ 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.", + Define.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) @@ -224,13 +224,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 = + 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 + Define.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/src/FSharp.Data.GraphQL.AspNet/AssemblyInfo.fs b/src/FSharp.Data.GraphQL.AspNet/AssemblyInfo.fs new file mode 100644 index 000000000..87986eda6 --- /dev/null +++ b/src/FSharp.Data.GraphQL.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.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.AspNet/FSharp.Data.GraphQL.AspNet.fsproj b/src/FSharp.Data.GraphQL.AspNet/FSharp.Data.GraphQL.AspNet.fsproj index 9edb021cb..db2fc0b30 100644 --- a/src/FSharp.Data.GraphQL.AspNet/FSharp.Data.GraphQL.AspNet.fsproj +++ b/src/FSharp.Data.GraphQL.AspNet/FSharp.Data.GraphQL.AspNet.fsproj @@ -4,6 +4,7 @@ preview + diff --git a/src/FSharp.Data.GraphQL.AspNet/GraphQL.fs b/src/FSharp.Data.GraphQL.AspNet/GraphQL.fs index b71be0668..1fb7dd971 100644 --- a/src/FSharp.Data.GraphQL.AspNet/GraphQL.fs +++ b/src/FSharp.Data.GraphQL.AspNet/GraphQL.fs @@ -46,14 +46,6 @@ type WebSocketServerMessage = | Error of id : string option * err : string | Complete of id : string -type FileUpload = - { Name : string - FileType : string - Size : int - Content : Stream - Path : string - Hash : string } - [] module Constants = let [] QueryJsonKey = "query" @@ -119,21 +111,6 @@ module private MultipartRequest = | _ -> NoContent - -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.") - ) - - module private JsonReader = let (|SpecificTypeDef|_|) (def: TypeDef) (value: TypeDef) = @@ -147,14 +124,34 @@ module private JsonReader = let getOptionCtors (optionInnerType: Type) = let optionType = typedefof> - let caseInfos = FSharpType.GetUnionCases(optionType.MakeGenericType(optionInnerType)) - let noneCtor = caseInfos |> Seq.find(fun case -> case.Name = "None") |> FSharpValue.PreComputeUnionConstructor - let someCtor = caseInfos |> Seq.find(fun case -> case.Name = "Some") |> FSharpValue.PreComputeUnionConstructor - someCtor, noneCtor + 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 - | _ -> failwith "Unexpected Quotation!" + | 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> + let rec getJsonReaderAux (input: InputDef) (cache: TypeReaderCache) = match input with @@ -177,12 +174,16 @@ module private JsonReader = member _.Read (replacements, path, element) = readerFunc.Value replacements path element })) and getNullableReader (innerDef: InputDef) (cache: TypeReaderCache) = - let innerReader: IJsonVariableReader = getJsonReader innerDef cache + let innerReader = getJsonReader innerDef cache let someCtor, noneCtor = getOptionCtors innerDef.Type fun replacements path (element: JsonElement) -> match element.ValueKind with - | JsonValueKind.Null -> noneCtor [||] - | _ -> someCtor [| innerReader.Read(replacements, path, element) |] + | 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 -> @@ -223,13 +224,13 @@ module private JsonReader = | 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: IJsonVariableReader = getJsonReader elementDef cache + let elementReader = getJsonReader elementDef cache let genericType = listDef.Type.GetGenericTypeDefinition() let collectionConverterMethInfo = - if genericType = typedefof> then getCallInfo <@ List.ofSeq<_> Seq.empty @> - elif genericType = typedefof> then getCallInfo <@ Array.ofSeq<_> Seq.empty @> - elif genericType = typedefof> then getCallInfo <@ Set.ofSeq<_> Seq.empty @> - else getCallInfo <@ Seq.cast<_> Seq.empty @> + 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|]) @@ -240,10 +241,7 @@ module private JsonReader = enumerator |> Seq.mapi(fun i value -> let elementPath = combinePath path (i.ToString()) - match Map.tryFind elementPath replacements with - | Some value -> box value - | None -> elementReader.Read(replacements, elementPath, value) ) - |> Array.ofSeq + elementReader.Read(replacements, elementPath, value) ) |> convertF | otherElement -> failwithf "Expected array element but received '%A'" otherElement @@ -253,11 +251,12 @@ module private JsonReader = if FSharpType.IsRecord inputObject.Type then let inputFieldReaders = inputObject.Fields - |> Array.map (fun field -> field.Name, (field, toCamelCase field.Name, getJsonReader field.TypeDef cache)) + |> 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 - match Map.tryFind field.Name inputFieldReaders with + 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) @@ -291,9 +290,6 @@ module private JsonReader = else failwithf "InputObject '%s' must be a Record type." inputObject.Type.Name - - - let readVariables (replacements: Map) (index: int option) (variablesJson: JsonElement option) (vars: list) = let readerCache = ConcurrentDictionary() let basePath = @@ -308,7 +304,7 @@ module private JsonReader = | true, value -> Some value | false, _ -> None | None -> - fun (name: string) -> None + fun _ -> None Map.ofList [ for var in vars do match tryReadProperty var.Name with @@ -325,6 +321,13 @@ module private JsonReader = ] 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) @@ -351,14 +354,14 @@ module private ExecutionHandler = let fileName = WebUtility.HtmlEncode(contentDisposition.FileName.Value) let filepath = Path.GetTempFileName() let cryptoHash = SHA256.Create() - let flags = FileOptions.Asynchronous ||| FileOptions.SequentialScan - let stream = new FileStream(filepath, FileMode.Create, FileAccess.Write, FileShare.None, 4096, flags) - use hash = new CryptoStream(stream, cryptoHash, CryptoStreamMode.Write) + 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; FileType = section.ContentType; Size = stream.Length |> int + { Name = fileName; ContentType = section.ContentType; Size = stream.Length |> int Path = filepath; Content = stream; Hash = bin2hex cryptoHash.Hash } return Some(contentDisposition.Name.Value, file) | _ -> @@ -374,40 +377,38 @@ module private ExecutionHandler = return! loop [] } - let rec readMappingAux (reader: byref) (files: Map) (accum: (string * FileUpload) list) = - match reader.TokenType with - | JsonTokenType.String -> - let mappedFile = reader.GetString() - let file = files.[mappedFile] - match reader.TokenType with - | JsonTokenType.StartArray -> - reader.Read() |> ignore - let path = reader.GetString() - reader.Read() |> ignore - readMappingAux &reader files ((path, file)::accum) - | invalidToken -> - let message = sprintf "Expected 'BeginArray' but received json token '%A'" invalidToken - raise <| invalidOp message - | JsonTokenType.EndObject -> - Map.ofSeq accum - | invalidToken -> - let message = sprintf "Expected PropertyName or EndObject for property mapping but received json token '%A'" invalidToken - raise <| invalidOp message - let readMapping (json: string) (fileMapping: Map) = let bytes = System.Text.Encoding.UTF8.GetBytes(json) let readonlySpan = ReadOnlySpan(bytes) - let mutable reader = Utf8JsonReader(readonlySpan) + let reader = Utf8JsonReader(readonlySpan) + let mapping = ResizeArray() + reader.Read() |> ignore match reader.TokenType with | JsonTokenType.StartObject -> reader.Read() |> ignore - readMappingAux &reader fileMapping [] + 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 (executor: Executor<'T>) (element: JsonElement) (replacements: Map) (index: int option) = match element.ValueKind with | JsonValueKind.Object -> @@ -418,17 +419,17 @@ module private ExecutionHandler = let variablesElement = match element.TryGetProperty(VariablesJsonKey) with - | true, value -> Some value - | false, value -> None - + | true, value when value.ValueKind <> JsonValueKind.Null -> Some value + | _ -> None let plan = executor.CreateExecutionPlan(queryText) + printfn "\nQuery: %s\nVariables: %A\nFiles: %A" queryText variablesElement replacements let variables = JsonReader.readVariables 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 (executor: Executor<'T>) (json: string) (replacements: Map) = + let readOperations (executor: Executor<'T>) (json: string) (replacements: Map) = use document = JsonDocument.Parse(json) match document.RootElement.ValueKind with | JsonValueKind.Array -> @@ -446,7 +447,7 @@ module private ExecutionHandler = do! JsonSerializer.SerializeAsync(outputStream, body, serializerOptions) |> Async.AwaitTask } - let processMultipartQuery (executor: Executor<'T>) (root: 'T) (ctx: HttpContext) (serializerOptions: JsonSerializerOptions) = async { + let processMultipartQuery (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 @@ -460,6 +461,7 @@ module private ExecutionHandler = match readOperations executor operationsJson replacements with | Single request -> let! response = executor.AsyncExecute(request.ExecutionPlan, data = root, variables = request.Variables) + printfn "%A" response do! serializeResponse ctx serializerOptions response | Batch requests -> let hasMutation = requests |> List.exists (fun req -> req.ExecutionPlan.Operation.OperationType = Ast.OperationType.Mutation) @@ -479,7 +481,7 @@ module private ExecutionHandler = return! reader.ReadToEndAsync() |> Async.AwaitTask } - let processQuery (executor: Executor<'T>) (root: 'T) (ctx: HttpContext) (serializerOptions: JsonSerializerOptions) = async { + let processQuery (executor: Executor<'T>) (root: 'T) (ctx: HttpContext) (serializerOptions: JsonSerializerOptions) = async { let! body = readBody ctx let! response = if System.String.IsNullOrWhiteSpace body then @@ -488,7 +490,9 @@ module private ExecutionHandler = async { use document = JsonDocument.Parse(body) let request = readOperation executor document.RootElement Map.empty None - return! executor.AsyncExecute(request.ExecutionPlan, data = root, variables = request.Variables) + let! result = executor.AsyncExecute(request.ExecutionPlan, data = root, variables = request.Variables) + printfn "%A" result + return result } let outputStream = ctx.Response.Body do! JsonSerializer.SerializeAsync(outputStream, response, serializerOptions) |> Async.AwaitTask @@ -515,5 +519,5 @@ module ApplicationBuilderExtensions = member builder.UseGraphQL(executor: Executor<'T>, buildArguments: HttpContext -> Async<'T>, ?path: string) = let urlPath = defaultArg path "/graphql" let graphQLHandler = ExecutionHandler.queryHandler buildArguments executor - builder.Map(PathString urlPath, (fun (builder:IApplicationBuilder) -> + 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.AspNet/Types.fs b/src/FSharp.Data.GraphQL.AspNet/Types.fs new file mode 100644 index 000000000..607b9d69b --- /dev/null +++ b/src/FSharp.Data.GraphQL.AspNet/Types.fs @@ -0,0 +1,25 @@ +namespace FSharp.Data.GraphQL.Types + +open System.IO + +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.AspNet/paket.references b/src/FSharp.Data.GraphQL.AspNet/paket.references index 6180f0615..31e1f774b 100644 --- a/src/FSharp.Data.GraphQL.AspNet/paket.references +++ b/src/FSharp.Data.GraphQL.AspNet/paket.references @@ -1,4 +1,4 @@ -nuget Common +group Common FSharp.Core group AspNetLib 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.Shared/Introspection.fs b/src/FSharp.Data.GraphQL.Shared/Introspection.fs index 296d0cc2e..160d9761e 100644 --- a/src/FSharp.Data.GraphQL.Shared/Introspection.fs +++ b/src/FSharp.Data.GraphQL.Shared/Introspection.fs @@ -183,7 +183,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 +193,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 +214,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 +248,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 +286,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 +302,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..69edd71b3 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,16 +7,10 @@ + - - - - - - - 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..322f0066e 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.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/LocalProviderTests.fs b/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderTests.fs index e6d5275e5..9b565a6fd 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderTests.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderTests.fs @@ -40,8 +40,6 @@ 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 @@ -60,7 +58,7 @@ module SimpleOperation = 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) @@ -489,4 +487,5 @@ let ``Should be able to upload files inside another input type``() = nullableMultiple = Array.map makeUpload request.NullableMultiple.Value, nullableMultipleNullable = Array.map (Option.map makeUpload) request.NullableMultipleNullable.Value) UploadRequestOperation.operation.Run(input) - |> UploadRequestOperation.validateResult request \ No newline at end of file + |> UploadRequestOperation.validateResult request +**) \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderWithOptionalParametersOnlyTests.fs b/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderWithOptionalParametersOnlyTests.fs index 24fdebb96..58cec8de4 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderWithOptionalParametersOnlyTests.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderWithOptionalParametersOnlyTests.fs @@ -40,8 +40,6 @@ 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 @@ -61,6 +59,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) @@ -198,6 +197,7 @@ let ``Should be able to execute a query using context, sending an an input field |> Async.RunSynchronously |> SimpleOperation.validateResult (Some input) + module SingleRequiredUploadOperation = let operation = Provider.Operation<"""mutation SingleUpload($file: Upload!) { @@ -211,8 +211,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 +242,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 +286,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 +322,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 +369,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 +454,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 +477,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/foo.txt b/tests/FSharp.Data.GraphQL.IntegrationTests/foo.txt new file mode 100644 index 000000000..285fc7c8f --- /dev/null +++ b/tests/FSharp.Data.GraphQL.IntegrationTests/foo.txt @@ -0,0 +1 @@ +fdsfdsf \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests/script.fsx b/tests/FSharp.Data.GraphQL.IntegrationTests/script.fsx new file mode 100644 index 000000000..622a69446 --- /dev/null +++ b/tests/FSharp.Data.GraphQL.IntegrationTests/script.fsx @@ -0,0 +1,67 @@ +#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/typeproviders/fsharp41/netstandard2.0/Microsoft.Extensions.Http.dll" +#r "../../src/FSharp.Data.GraphQL.Client/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 +open System.IO + +let [] ServerUrl = "http://localhost:8085" +let [] EmptyGuidAsString = "00000000-0000-0000-0000-000000000000" + +type Provider = GraphQLProvider + +let context = Provider.GetContext(ServerUrl) + +type Input = Provider.Types.Input +type InputField = Provider.Types.InputField + +let operation = + Provider.Operation<"""query Q($input: Input) { + echo(input: $input) { + single { + ...Field + } + list { + ...Field + } + } + } + + fragment Field on OutputField { + string + stringOption + int + intOption + uri + deprecated + guid + }""">() +let fileOperation = + Provider.Operation<"""mutation SingleUpload($file: Upload!) { + singleUpload(file: $file) { + name + contentType + contentAsText + } + }""">() + +let filesOperation = + Provider.Operation<"""mutation nullablesMultipleNullableUpload($files: [Upload]) { + nullableMultipleNullableUpload(files: $files) { + name + contentType + contentAsText + } + }""">() + + +let bytes = "Foo Bar"B +let upload = new Upload(bytes, "foo.txt") +let result = fileOperation.Run(upload) +result.Data + + + + +let uploads = [| Some(new Upload("Foo Bar"B, "foo.txt")); None|] +let results = filesOperation.Run(uploads) 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..ed6de8142 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) From 19aac5e7de9368cae62ca8c0255c1d7405045e93 Mon Sep 17 00:00:00 2001 From: John Berzy Date: Fri, 23 Apr 2021 00:01:46 -0700 Subject: [PATCH 4/8] add asp.net package --- samples/client-provider/field_aliases.fsx | 16 +- samples/client-provider/file-upload/README.md | 9 - .../file-upload/multiple_files_upload.fsx | 44 - .../client-provider/file-upload/server/.env | 2 - .../file-upload/server/.gitignore | 2 - .../file-upload/server/package-lock.json | 5033 ----------------- .../file-upload/server/package.json | 52 - .../file-upload/server/resolvers.mjs | 70 - .../file-upload/server/server.mjs | 29 - .../file-upload/server/types.mjs | 25 - .../file-upload/single_file_upload.fsx | 40 - .../file-upload/uploaded_files.fsx | 36 - samples/client-provider/github_access.fsx | 16 +- samples/client-provider/graphql_client.fsx | 18 +- .../http_headers_introspection.fsx | 18 +- samples/client-provider/mutations.fsx | 18 +- samples/client-provider/parameters.fsx | 18 +- samples/client-provider/short_hand_query.fsx | 18 +- samples/client-provider/standard_features.fsx | 18 +- samples/client-provider/using_query_file.fsx | 16 +- samples/file-uploads/Schema.fs | 71 +- .../client-provider/multiple_files_upload.fsx | 66 + .../client-provider}/png_file.png | Bin .../client-provider}/txt_file.txt | 0 .../FSharp.Data.GraphQL.AspNet.fsproj | 1 + src/FSharp.Data.GraphQL.AspNet/GraphQL.fs | 325 +- src/FSharp.Data.GraphQL.AspNet/Json.fs | 256 + src/FSharp.Data.GraphQL.AspNet/Types.fs | 25 + .../GraphQLProvider.DesignTime.fs | 9 - src/FSharp.Data.GraphQL.Server/Values.fs | 7 +- .../LocalProviderTests.fs | 17 +- .../script.fsx | 67 - 32 files changed, 432 insertions(+), 5910 deletions(-) delete mode 100644 samples/client-provider/file-upload/README.md delete mode 100644 samples/client-provider/file-upload/multiple_files_upload.fsx delete mode 100644 samples/client-provider/file-upload/server/.env delete mode 100644 samples/client-provider/file-upload/server/.gitignore delete mode 100644 samples/client-provider/file-upload/server/package-lock.json delete mode 100644 samples/client-provider/file-upload/server/package.json delete mode 100644 samples/client-provider/file-upload/server/resolvers.mjs delete mode 100644 samples/client-provider/file-upload/server/server.mjs delete mode 100644 samples/client-provider/file-upload/server/types.mjs delete mode 100644 samples/client-provider/file-upload/single_file_upload.fsx delete mode 100644 samples/client-provider/file-upload/uploaded_files.fsx create mode 100644 samples/file-uploads/client-provider/multiple_files_upload.fsx rename samples/{client-provider/file-upload => file-uploads/client-provider}/png_file.png (100%) rename samples/{client-provider/file-upload => file-uploads/client-provider}/txt_file.txt (100%) create mode 100644 src/FSharp.Data.GraphQL.AspNet/Json.fs delete mode 100644 tests/FSharp.Data.GraphQL.IntegrationTests/script.fsx 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/github_access.fsx b/samples/client-provider/github_access.fsx index 44e50aea6..1fe505f62 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 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/samples/file-uploads/Schema.fs b/samples/file-uploads/Schema.fs index 8fcbdc3d1..d8b89c7ba 100644 --- a/samples/file-uploads/Schema.fs +++ b/samples/file-uploads/Schema.fs @@ -4,30 +4,43 @@ open FSharp.Data.GraphQL.Types open type FSharp.Data.GraphQL.Types.SchemaDefinitions.Define open FSharp.Data.GraphQL.AspNet -type Uploads = - { Files : FSharp.Data.GraphQL.AspNet.FileUpload list option } +type UploadsInput = + { Files : FileUpload list } + +type FileInfo = + { Hash: string + Path: string + FileName: string + ContentType: string } type Root = unit module Schema = - let UploadType = - 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 an AST input.") - ) + let mkFileInfo (upload: FileUpload) = + { Hash = upload.Hash + Path = upload.Path + FileName = upload.Name + ContentType = upload.ContentType } + let UploadsType = - InputObject( + InputObject( name = "Uploads", description = "The `Upload` scalar type represents a file upload.", fields = [ - Input("files", Nullable(ListOf UploadType)) + 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) ] ) @@ -39,28 +52,24 @@ module Schema = ] ) - let Mutation = Object( name = "Mutation", fields = [ - AsyncField("singleUpload", Boolean, "upload a single file", [Input("file", UploadType)], - fun ctx _ -> async { + Field("singleUpload", FileInfoType, "upload a single file", [Input("file", FileUpload)], + fun ctx _ -> let file = ctx.Arg("file") - printfn $"File: {file}" - return true - }) - AsyncField("multiUpload", Boolean, "upload a single file", [Input("files", ListOf UploadType)], - fun ctx _ -> async { + mkFileInfo file + ) + Field("multiUpload", ListOf FileInfoType, "upload a single file", [Input("files", ListOf FileUpload)], + fun ctx _ -> let files = ctx.Arg("files") - printfn $"Files: {files}" - return true - }) - AsyncField("multiUploadRecord", Boolean, "upload a single file", [Input("files", UploadsType)], - fun ctx _ -> async { + files |> List.map mkFileInfo + ) + Field("multiUploadRecord", ListOf FileInfoType, "upload a single file", [Input("files", UploadsType)], + fun ctx _ -> let files = ctx.Arg("files") - printfn $"Files: {files}" - return true - }) + files.Files |> List.map mkFileInfo + ) ] ) \ No newline at end of file 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/src/FSharp.Data.GraphQL.AspNet/FSharp.Data.GraphQL.AspNet.fsproj b/src/FSharp.Data.GraphQL.AspNet/FSharp.Data.GraphQL.AspNet.fsproj index db2fc0b30..9c868909c 100644 --- a/src/FSharp.Data.GraphQL.AspNet/FSharp.Data.GraphQL.AspNet.fsproj +++ b/src/FSharp.Data.GraphQL.AspNet/FSharp.Data.GraphQL.AspNet.fsproj @@ -5,6 +5,7 @@ + diff --git a/src/FSharp.Data.GraphQL.AspNet/GraphQL.fs b/src/FSharp.Data.GraphQL.AspNet/GraphQL.fs index 1fb7dd971..93bd348b2 100644 --- a/src/FSharp.Data.GraphQL.AspNet/GraphQL.fs +++ b/src/FSharp.Data.GraphQL.AspNet/GraphQL.fs @@ -3,18 +3,12 @@ namespace FSharp.Data.GraphQL.AspNet open System open System.IO open System.Net -open System.Reflection open System.Text.Json -open System.Text.Json.Serialization open System.Threading.Tasks open System.Security.Cryptography -open System.Collections.Concurrent -open FSharp.Reflection open FSharp.Data.GraphQL -open FSharp.Data.GraphQL.Execution open FSharp.Data.GraphQL.Types -open FSharp.Data.GraphQL.Types.Patterns open Microsoft.AspNetCore.Builder open Microsoft.AspNetCore.Http @@ -22,70 +16,6 @@ open Microsoft.Net.Http.Headers open Microsoft.Extensions.Primitives open Microsoft.AspNetCore.WebUtilities open Microsoft.AspNetCore.Http.Extensions -open Microsoft.AspNetCore.Http.Features - -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 - -[] -module Constants = - let [] QueryJsonKey = "query" - let [] VariablesJsonKey = "variables" - let [] DataJsonKey = "data" - -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(DataJsonKey) - -type IJsonVariableReader = - abstract IsNullable : bool - abstract Read : replacements: Map * path: string * element: JsonElement -> obj - -type TypeReaderCache = ConcurrentDictionary - [] module private MultipartRequest = @@ -104,221 +34,12 @@ module private MultipartRequest = 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 + if contentDisposition.IsFormDisposition() + then FormContent contentDisposition + else FileContent contentDisposition | _ -> NoContent -module private JsonReader = - - 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> - - - 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 readVariables (replacements: Map) (index: int option) (variablesJson: JsonElement option) (vars: list) = - let readerCache = ConcurrentDictionary() - let basePath = - match index with - | Some idx -> combinePath (idx.ToString()) VariablesJsonKey - | None -> VariablesJsonKey - 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 -> - let path = combinePath basePath var.Name - let reader = getJsonReader var.TypeDef readerCache - let variableValue = reader.Read(replacements, path, element) - (var.Name, variableValue) - | None -> - match var.DefaultValue, var.TypeDef with - | Some _, _ -> () - | _, Nullable _ -> () - | _ -> failwithf "Variable '%s' is missing and there is no default value" var.Name - ] module private ExecutionHandler = // crypto stream that doesn't close underlying stream @@ -409,37 +130,35 @@ module private ExecutionHandler = let message = sprintf "Expected PropertyName or EndObject for property mapping but received json token '%A'" invalidToken raise <| invalidOp message - let readOperation (executor: Executor<'T>) (element: JsonElement) (replacements: Map) (index: int option) = + 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(QueryJsonKey) with + match element.TryGetProperty("query") with | true, queryElement -> queryElement.GetString() | false, _ -> Introspection.IntrospectionQuery - let variablesElement = - match element.TryGetProperty(VariablesJsonKey) with + match element.TryGetProperty("variables") with | true, value when value.ValueKind <> JsonValueKind.Null -> Some value | _ -> None let plan = executor.CreateExecutionPlan(queryText) - printfn "\nQuery: %s\nVariables: %A\nFiles: %A" queryText variablesElement replacements - let variables = JsonReader.readVariables replacements index variablesElement plan.Variables + 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 (executor: Executor<'T>) (json: string) (replacements: Map) = + 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 executor operation replacements (Some i)) + |> Seq.mapi(fun i operation -> readOperation cache executor operation replacements (Some i)) |> Seq.toList Batch operations | _ -> - let operation = readOperation executor document.RootElement replacements None + let operation = readOperation cache executor document.RootElement replacements None Single operation let serializeResponse (ctx: HttpContext) (serializerOptions: JsonSerializerOptions) (body: 'T) = async { @@ -447,7 +166,7 @@ module private ExecutionHandler = do! JsonSerializer.SerializeAsync(outputStream, body, serializerOptions) |> Async.AwaitTask } - let processMultipartQuery (executor: Executor<'T>) (root: 'T) (ctx: HttpContext) (serializerOptions: JsonSerializerOptions) = async { + 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 @@ -458,10 +177,9 @@ module private ExecutionHandler = match operationPart with | Some("operations", operationsJson) -> let replacements = readMapping mappingJson fileParts - match readOperations executor operationsJson replacements with + match readOperations cache executor operationsJson replacements with | Single request -> let! response = executor.AsyncExecute(request.ExecutionPlan, data = root, variables = request.Variables) - printfn "%A" response do! serializeResponse ctx serializerOptions response | Batch requests -> let hasMutation = requests |> List.exists (fun req -> req.ExecutionPlan.Operation.OperationType = Ast.OperationType.Mutation) @@ -481,7 +199,7 @@ module private ExecutionHandler = return! reader.ReadToEndAsync() |> Async.AwaitTask } - let processQuery (executor: Executor<'T>) (root: 'T) (ctx: HttpContext) (serializerOptions: JsonSerializerOptions) = async { + 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 @@ -489,19 +207,17 @@ module private ExecutionHandler = else async { use document = JsonDocument.Parse(body) - let request = readOperation executor document.RootElement Map.empty None - let! result = executor.AsyncExecute(request.ExecutionPlan, data = root, variables = request.Variables) - printfn "%A" result - return result + 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>) = + let queryHandler (buildArguments: HttpContext -> Async<'T>) (executor: Executor<'T>) (readerCache: Json.TypeReaderCache) = let graphQLSerializationOptions = JsonSerializerOptions() - graphQLSerializationOptions.Converters.Add(GQLResponseConverter()) - graphQLSerializationOptions.Converters.Add(NameValueLookupConverter()) + graphQLSerializationOptions.Converters.Add(Json.GQLResponseConverter()) + graphQLSerializationOptions.Converters.Add(Json.NameValueLookupConverter()) fun (ctx: HttpContext) -> async { ctx.Response.Headers.Add("Content-Type", StringValues("application/json")) @@ -510,7 +226,7 @@ module private ExecutionHandler = if isMultipartContentType ctx.Request.ContentType then processMultipartQuery else processQuery - do! execute executor root ctx graphQLSerializationOptions + do! execute readerCache executor root ctx graphQLSerializationOptions } |> Async.StartAsTask :> Task [] @@ -518,6 +234,7 @@ module ApplicationBuilderExtensions = type IApplicationBuilder with member builder.UseGraphQL(executor: Executor<'T>, buildArguments: HttpContext -> Async<'T>, ?path: string) = let urlPath = defaultArg path "/graphql" - let graphQLHandler = ExecutionHandler.queryHandler buildArguments executor + 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.AspNet/Json.fs b/src/FSharp.Data.GraphQL.AspNet/Json.fs new file mode 100644 index 000000000..2f8420d67 --- /dev/null +++ b/src/FSharp.Data.GraphQL.AspNet/Json.fs @@ -0,0 +1,256 @@ +module internal FSharp.Data.GraphQL.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.AspNet/Types.fs b/src/FSharp.Data.GraphQL.AspNet/Types.fs index 607b9d69b..ed149e207 100644 --- a/src/FSharp.Data.GraphQL.AspNet/Types.fs +++ b/src/FSharp.Data.GraphQL.AspNet/Types.fs @@ -1,6 +1,31 @@ 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 diff --git a/src/FSharp.Data.GraphQL.Client.DesignTime/GraphQLProvider.DesignTime.fs b/src/FSharp.Data.GraphQL.Client.DesignTime/GraphQLProvider.DesignTime.fs index 8dc8c50a7..587970763 100644 --- a/src/FSharp.Data.GraphQL.Client.DesignTime/GraphQLProvider.DesignTime.fs +++ b/src/FSharp.Data.GraphQL.Client.DesignTime/GraphQLProvider.DesignTime.fs @@ -13,7 +13,6 @@ 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) @@ -22,13 +21,5 @@ type GraphQLTypeProvider (config) as this = 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 - [] do() \ No newline at end of file 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/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderTests.fs b/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderTests.fs index 9b565a6fd..db39b351b 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderTests.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests/LocalProviderTests.fs @@ -58,7 +58,7 @@ module SimpleOperation = 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) @@ -209,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 @@ -242,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 @@ -288,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 @@ -326,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 @@ -375,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 @@ -462,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 @@ -487,5 +475,4 @@ let ``Should be able to upload files inside another input type``() = nullableMultiple = Array.map makeUpload request.NullableMultiple.Value, nullableMultipleNullable = Array.map (Option.map makeUpload) request.NullableMultipleNullable.Value) UploadRequestOperation.operation.Run(input) - |> UploadRequestOperation.validateResult request -**) \ No newline at end of file + |> UploadRequestOperation.validateResult request \ No newline at end of file diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests/script.fsx b/tests/FSharp.Data.GraphQL.IntegrationTests/script.fsx deleted file mode 100644 index 622a69446..000000000 --- a/tests/FSharp.Data.GraphQL.IntegrationTests/script.fsx +++ /dev/null @@ -1,67 +0,0 @@ -#r "../../src/FSharp.Data.GraphQL.Client/bin/Debug/typeproviders/fsharp41/netstandard2.0/Microsoft.Extensions.Http.dll" -#r "../../src/FSharp.Data.GraphQL.Client/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 -open System.IO - -let [] ServerUrl = "http://localhost:8085" -let [] EmptyGuidAsString = "00000000-0000-0000-0000-000000000000" - -type Provider = GraphQLProvider - -let context = Provider.GetContext(ServerUrl) - -type Input = Provider.Types.Input -type InputField = Provider.Types.InputField - -let operation = - Provider.Operation<"""query Q($input: Input) { - echo(input: $input) { - single { - ...Field - } - list { - ...Field - } - } - } - - fragment Field on OutputField { - string - stringOption - int - intOption - uri - deprecated - guid - }""">() -let fileOperation = - Provider.Operation<"""mutation SingleUpload($file: Upload!) { - singleUpload(file: $file) { - name - contentType - contentAsText - } - }""">() - -let filesOperation = - Provider.Operation<"""mutation nullablesMultipleNullableUpload($files: [Upload]) { - nullableMultipleNullableUpload(files: $files) { - name - contentType - contentAsText - } - }""">() - - -let bytes = "Foo Bar"B -let upload = new Upload(bytes, "foo.txt") -let result = fileOperation.Run(upload) -result.Data - - - - -let uploads = [| Some(new Upload("Foo Bar"B, "foo.txt")); None|] -let results = filesOperation.Run(uploads) From 508250c2f7e44ef26d8f0bf21713141033b1a796 Mon Sep 17 00:00:00 2001 From: John Berzy Date: Fri, 23 Apr 2021 00:10:55 -0700 Subject: [PATCH 5/8] copy paste typo --- samples/file-uploads/Schema.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/file-uploads/Schema.fs b/samples/file-uploads/Schema.fs index d8b89c7ba..f9b1afcb9 100644 --- a/samples/file-uploads/Schema.fs +++ b/samples/file-uploads/Schema.fs @@ -48,7 +48,7 @@ module Schema = Object( name = "Query", fields = [ - Field("Message", String, "A list of the employees", fun ctx _ -> "Hello World") + Field("Message", String, "A greeting", fun ctx _ -> "Hello World") ] ) From 9c5b02f8fcdd809b2b4d855cdbea6f4858804f9a Mon Sep 17 00:00:00 2001 From: John Berzy Date: Fri, 23 Apr 2021 00:45:02 -0700 Subject: [PATCH 6/8] use the open type syntax for sample --- samples/file-uploads/Schema.fs | 2 +- samples/star-wars-api/Schema.fs | 77 +++++++++++++++++---------------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/samples/file-uploads/Schema.fs b/samples/file-uploads/Schema.fs index f9b1afcb9..9e5365182 100644 --- a/samples/file-uploads/Schema.fs +++ b/samples/file-uploads/Schema.fs @@ -1,8 +1,8 @@ namespace FSharp.Data.GraphQL.Samples.FileUpload open FSharp.Data.GraphQL.Types -open type FSharp.Data.GraphQL.Types.SchemaDefinitions.Define open FSharp.Data.GraphQL.AspNet +open type FSharp.Data.GraphQL.Types.SchemaDefinitions.Define type UploadsInput = { Files : FileUpload list } diff --git a/samples/star-wars-api/Schema.fs b/samples/star-wars-api/Schema.fs index 6f06462a3..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", SeqOf (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", SeqOf (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 -> @@ -225,7 +226,7 @@ module Schema = schemaConfig.LiveFieldSubscriptionProvider.Publish "Planet" "isMoon" x x))]) let middlewares = - [ Define.QueryWeightMiddleware(2.0, true) - Define.ObjectListFilterMiddleware(true) - Define.ObjectListFilterMiddleware(true) - Define.LiveQueryMiddleware() ] \ No newline at end of file + [ QueryWeightMiddleware(2.0, true) + ObjectListFilterMiddleware(true) + ObjectListFilterMiddleware(true) + LiveQueryMiddleware() ] \ No newline at end of file From 01239927e3874b27b56594333b9c30dd004e212f Mon Sep 17 00:00:00 2001 From: John Berzy Date: Fri, 23 Apr 2021 20:16:08 -0700 Subject: [PATCH 7/8] Packaging and renaming --- FSharp.Data.GraphQL.sln | 2 +- build.fsx | 4 ++++ ...arp.Data.GraphQL.Samples.FileUpload.fsproj | 2 +- samples/file-uploads/Program.fs | 2 +- samples/file-uploads/Schema.fs | 2 +- ...rp.Data.GraphQL.Samples.StarWarsApi.fsproj | 2 +- samples/star-wars-api/Program.fs | 2 +- .../AssemblyInfo.fs | 4 ++-- .../FSharp.Data.GraphQL.Server.AspNet.fsproj} | 0 ...raphQL.Server.AspNet.fsproj.paket.template | 24 +++++++++++++++++++ .../GraphQL.fs | 2 +- .../Json.fs | 2 +- .../Types.fs | 0 .../paket.references | 0 ...ata.GraphQL.IntegrationTests.Server.fsproj | 2 +- .../Program.fs | 2 +- 16 files changed, 40 insertions(+), 12 deletions(-) rename src/{FSharp.Data.GraphQL.AspNet => FSharp.Data.GraphQL.Server.AspNet}/AssemblyInfo.fs (81%) rename src/{FSharp.Data.GraphQL.AspNet/FSharp.Data.GraphQL.AspNet.fsproj => FSharp.Data.GraphQL.Server.AspNet/FSharp.Data.GraphQL.Server.AspNet.fsproj} (100%) create mode 100644 src/FSharp.Data.GraphQL.Server.AspNet/FSharp.Data.GraphQL.Server.AspNet.fsproj.paket.template rename src/{FSharp.Data.GraphQL.AspNet => FSharp.Data.GraphQL.Server.AspNet}/GraphQL.fs (99%) rename src/{FSharp.Data.GraphQL.AspNet => FSharp.Data.GraphQL.Server.AspNet}/Json.fs (99%) rename src/{FSharp.Data.GraphQL.AspNet => FSharp.Data.GraphQL.Server.AspNet}/Types.fs (100%) rename src/{FSharp.Data.GraphQL.AspNet => FSharp.Data.GraphQL.Server.AspNet}/paket.references (100%) diff --git a/FSharp.Data.GraphQL.sln b/FSharp.Data.GraphQL.sln index cefc8458a..f77df7cda 100644 --- a/FSharp.Data.GraphQL.sln +++ b/FSharp.Data.GraphQL.sln @@ -137,7 +137,7 @@ 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.AspNet", "src\FSharp.Data.GraphQL.AspNet\FSharp.Data.GraphQL.AspNet.fsproj", "{F5640838-93AB-4903-822B-D31D4D0FEBE2}" +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 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/samples/file-uploads/FSharp.Data.GraphQL.Samples.FileUpload.fsproj b/samples/file-uploads/FSharp.Data.GraphQL.Samples.FileUpload.fsproj index a5fb8e0ce..6f92f7232 100644 --- a/samples/file-uploads/FSharp.Data.GraphQL.Samples.FileUpload.fsproj +++ b/samples/file-uploads/FSharp.Data.GraphQL.Samples.FileUpload.fsproj @@ -8,7 +8,7 @@ - + diff --git a/samples/file-uploads/Program.fs b/samples/file-uploads/Program.fs index cc162e915..3fd6aa299 100644 --- a/samples/file-uploads/Program.fs +++ b/samples/file-uploads/Program.fs @@ -11,7 +11,7 @@ open Microsoft.Extensions.Logging open Microsoft.Extensions.DependencyInjection open FSharp.Data.GraphQL open FSharp.Data.GraphQL.Server -open FSharp.Data.GraphQL.AspNet +open FSharp.Data.GraphQL.Server.AspNet module Program = let configureApp (app : IApplicationBuilder) = diff --git a/samples/file-uploads/Schema.fs b/samples/file-uploads/Schema.fs index 9e5365182..50278b1c8 100644 --- a/samples/file-uploads/Schema.fs +++ b/samples/file-uploads/Schema.fs @@ -1,7 +1,7 @@ namespace FSharp.Data.GraphQL.Samples.FileUpload open FSharp.Data.GraphQL.Types -open FSharp.Data.GraphQL.AspNet +open FSharp.Data.GraphQL.Server.AspNet open type FSharp.Data.GraphQL.Types.SchemaDefinitions.Define type UploadsInput = 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 a5fb8e0ce..6f92f7232 100644 --- a/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj +++ b/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj @@ -8,7 +8,7 @@ - + diff --git a/samples/star-wars-api/Program.fs b/samples/star-wars-api/Program.fs index 565ccfbdd..1591c0953 100644 --- a/samples/star-wars-api/Program.fs +++ b/samples/star-wars-api/Program.fs @@ -11,7 +11,7 @@ open Microsoft.Extensions.Logging open Microsoft.Extensions.DependencyInjection open FSharp.Data.GraphQL open FSharp.Data.GraphQL.Server -open FSharp.Data.GraphQL.AspNet +open FSharp.Data.GraphQL.Server.AspNet module Program = let configureApp (app : IApplicationBuilder) = diff --git a/src/FSharp.Data.GraphQL.AspNet/AssemblyInfo.fs b/src/FSharp.Data.GraphQL.Server.AspNet/AssemblyInfo.fs similarity index 81% rename from src/FSharp.Data.GraphQL.AspNet/AssemblyInfo.fs rename to src/FSharp.Data.GraphQL.Server.AspNet/AssemblyInfo.fs index 87986eda6..e76544d03 100644 --- a/src/FSharp.Data.GraphQL.AspNet/AssemblyInfo.fs +++ b/src/FSharp.Data.GraphQL.Server.AspNet/AssemblyInfo.fs @@ -2,7 +2,7 @@ namespace System open System.Reflection -[] +[] [] [] [] @@ -10,7 +10,7 @@ open System.Reflection do () module internal AssemblyVersionInformation = - let [] AssemblyTitle = "FSharp.Data.GraphQL.AspNet" + 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" diff --git a/src/FSharp.Data.GraphQL.AspNet/FSharp.Data.GraphQL.AspNet.fsproj b/src/FSharp.Data.GraphQL.Server.AspNet/FSharp.Data.GraphQL.Server.AspNet.fsproj similarity index 100% rename from src/FSharp.Data.GraphQL.AspNet/FSharp.Data.GraphQL.AspNet.fsproj rename to src/FSharp.Data.GraphQL.Server.AspNet/FSharp.Data.GraphQL.Server.AspNet.fsproj 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.AspNet/GraphQL.fs b/src/FSharp.Data.GraphQL.Server.AspNet/GraphQL.fs similarity index 99% rename from src/FSharp.Data.GraphQL.AspNet/GraphQL.fs rename to src/FSharp.Data.GraphQL.Server.AspNet/GraphQL.fs index 93bd348b2..0b9b9ca11 100644 --- a/src/FSharp.Data.GraphQL.AspNet/GraphQL.fs +++ b/src/FSharp.Data.GraphQL.Server.AspNet/GraphQL.fs @@ -1,4 +1,4 @@ -namespace FSharp.Data.GraphQL.AspNet +namespace FSharp.Data.GraphQL.Server.AspNet open System open System.IO diff --git a/src/FSharp.Data.GraphQL.AspNet/Json.fs b/src/FSharp.Data.GraphQL.Server.AspNet/Json.fs similarity index 99% rename from src/FSharp.Data.GraphQL.AspNet/Json.fs rename to src/FSharp.Data.GraphQL.Server.AspNet/Json.fs index 2f8420d67..c69f24b7d 100644 --- a/src/FSharp.Data.GraphQL.AspNet/Json.fs +++ b/src/FSharp.Data.GraphQL.Server.AspNet/Json.fs @@ -1,4 +1,4 @@ -module internal FSharp.Data.GraphQL.AspNet.Json +module internal FSharp.Data.GraphQL.Server.AspNet.Json open System open System.Reflection diff --git a/src/FSharp.Data.GraphQL.AspNet/Types.fs b/src/FSharp.Data.GraphQL.Server.AspNet/Types.fs similarity index 100% rename from src/FSharp.Data.GraphQL.AspNet/Types.fs rename to src/FSharp.Data.GraphQL.Server.AspNet/Types.fs diff --git a/src/FSharp.Data.GraphQL.AspNet/paket.references b/src/FSharp.Data.GraphQL.Server.AspNet/paket.references similarity index 100% rename from src/FSharp.Data.GraphQL.AspNet/paket.references rename to src/FSharp.Data.GraphQL.Server.AspNet/paket.references 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 69edd71b3..d7da902a6 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,7 +7,7 @@ - + diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Program.fs b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Program.fs index 322f0066e..a3e25a8ee 100644 --- a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Program.fs +++ b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/Program.fs @@ -12,7 +12,7 @@ open Microsoft.Extensions.Logging open Microsoft.Extensions.DependencyInjection open FSharp.Data.GraphQL open FSharp.Data.GraphQL.Server -open FSharp.Data.GraphQL.AspNet +open FSharp.Data.GraphQL.Server.AspNet module Program = let configureApp (app : IApplicationBuilder) = From 94d1d19d753ce58e6b7c4fbd3e2db67ca7695a1b Mon Sep 17 00:00:00 2001 From: John Berzy Date: Fri, 23 Apr 2021 20:21:34 -0700 Subject: [PATCH 8/8] Remove Application Insights --- .../file-uploads/FSharp.Data.GraphQL.Samples.FileUpload.fsproj | 1 - .../star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj | 1 - .../ApplicationInsights.config | 0 .../FSharp.Data.GraphQL.IntegrationTests.Server.fsproj | 1 - 4 files changed, 3 deletions(-) delete mode 100644 tests/FSharp.Data.GraphQL.IntegrationTests.Server/ApplicationInsights.config diff --git a/samples/file-uploads/FSharp.Data.GraphQL.Samples.FileUpload.fsproj b/samples/file-uploads/FSharp.Data.GraphQL.Samples.FileUpload.fsproj index 6f92f7232..d256f1b3a 100644 --- a/samples/file-uploads/FSharp.Data.GraphQL.Samples.FileUpload.fsproj +++ b/samples/file-uploads/FSharp.Data.GraphQL.Samples.FileUpload.fsproj @@ -14,7 +14,6 @@ - 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 6f92f7232..d256f1b3a 100644 --- a/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj +++ b/samples/star-wars-api/FSharp.Data.GraphQL.Samples.StarWarsApi.fsproj @@ -14,7 +14,6 @@ - diff --git a/tests/FSharp.Data.GraphQL.IntegrationTests.Server/ApplicationInsights.config b/tests/FSharp.Data.GraphQL.IntegrationTests.Server/ApplicationInsights.config deleted file mode 100644 index e69de29bb..000000000 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 d7da902a6..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 @@ -13,7 +13,6 @@ -