moonbitstack/moongql/exec does not have a README file

    Extension

    pub(open) trait Extension {
    fn on_field(Self, ResolveInfo) -> Unit raise FieldError
    fn on_result(Self, ResolveInfo,
    Json
    ) ->
    Json

    }

    A hook into field resolution, applied to every field: on_field before the resolver runs (it may refuse, and the refusal is a field error at that field's path), on_result after the value is completed (it may replace it).

    Tracing, metrics, a per-field cache and Relay's global-id rewriting are all extensions, so the engine stays free of whichever ones a program happens to want.

    FromGql

    pub(open) trait FromGql {
    fn from_gql(
    Json
    ) -> Self raise FieldError
    }

    A MoonBit value read from an argument or a parent value: async-graphql's InputType. A refusal is a FieldError saying what was expected.
    impl FromGql for Bool
    impl FromGql for Int
    impl FromGql for Int64
    impl FromGql for Double
    impl FromGql for String
    impl FromGql for Option[T]
    impl FromGql for Array[T]

    ToGql

    pub(open) trait ToGql {
    fn to_gql(Self) ->
    Json

    }

    A MoonBit value as a GraphQL result: async-graphql's OutputType. Implemented for the built-in scalars, Int64 (written as a string, since GraphQL's Int is 32-bit), Json, T? and Array[T]; the #gql generator writes it for each annotated type.
    impl ToGql for Bool
    impl ToGql for Int
    impl ToGql for Int64
    impl ToGql for Double
    impl ToGql for String
    impl ToGql for Option[T]
    impl ToGql for Array[T]

    FieldError

    pub(all) suberror FieldError {
    FieldError(String)
    FieldErrorExt(String, Map[String,
    Json
    ])
    }

    An error raised by a field resolver; its message is reported in the response errors list with the field's response path.

    FieldError::expected

    fn FieldError::expected(want : String, got :
    Json
    ) -> FieldError

    The refusal of a value that is not a want, as every [FromGql] here writes it: expected Int, got "x".

    FieldError::message

    fn FieldError::message(self : FieldError) -> String

    The sentence a field error carries, whichever variant it is.

    Limits

    pub(all) struct Limits {
    max_concurrency : Int
    max_defer : Int
    max_stream : Int
    }

    A limit on how much work one request may start. A deferral past max_defer or a stream past max_stream is a field error rather than an unbounded run.

    Live

    pub struct Live {
    next_payload : async () ->
    Json
    ?
    stop : () -> Unit
    }

    An established subscription over a live source. next answers the next response payload, or None when the source ended; close stops the source.

    Establishing one parses, validates and coerce exactly as execute_subscription does, so a request that could never work is refused with its errors before any stream is opened — which is what lets a transport answer a single error message instead of an empty next run.

    Live::close

    fn Live::close(self : Live) -> Unit

    Stop the subscription: the source is closed and a pending next answers None.

    Live::next

    async fn Live::next(self : Live) ->
    Json
    ?

    The next payload of the subscription, or None when the source has ended.

    ResolveInfo

    pub(all) struct ResolveInfo {
    parent :
    Json

    args : Map[String,
    Json
    ]
    ctx :
    Json

    field_name : String
    selection : Array[String]
    }

    The information a field resolver receives: the resolved parent object (as JSON), the coerced arguments, the shared context value, and the field's name. A resolver returns the field's value as JSON (an object for composite types, whose sub-fields are then resolved against it) and may raise FieldError to surface a field error.

    ResolveInfo::arg

    fn ResolveInfo::arg(self : ResolveInfo, name : String) ->
    Json

    Read a named argument as JSON, or Json::null() when it was not supplied.

    ResolveInfo::read

    fn[T : FromGql] ResolveInfo::read(self : ResolveInfo, name : String) -> T raise FieldError

    The argument name as any [FromGql] type; a missing argument reads as null, so it is None for a T?. A refusal names the argument.

    ResolveInfo::reads

    fn ResolveInfo::reads(self : ResolveInfo, name : String) -> Bool

    Whether the document asks for the response key name under this field. A resolver that builds a query from the request — the ORM projection, a Relay node — asks this rather than fetching everything.

    Resolvers

    pub struct Resolvers {
    map : Map[String, (ResolveInfo) ->
    Json
    raise FieldError]
    async_map : Map[String, async (ResolveInfo) ->
    Json
    ]
    permissions : Map[String, (ResolveInfo) -> Bool]
    extensions : Array[&Extension]
    type_hooks : Map[String, (
    Json
    ,
    Json
    ) -> String?]
    instance_hooks : Map[String, (
    Json
    ,
    Json
    ) -> Bool]
    }

    A registry of field resolvers, keyed by "TypeName.fieldName". Fields with no registered resolver fall back to the default resolver, which reads the field's name off the parent JSON object (matching graphql-core's default_field_resolver).

    It also holds the two hooks that answer "which object type is this value?" at an interface or union position, the equivalents of graphql-core's resolve_type and is_type_of.

    Resolvers::field

    fn Resolvers::field(self : Resolvers, type_name : String, field_name : String, resolver : (ResolveInfo) ->
    Json
    raise FieldError) -> Unit

    Register resolver for field field_name on type type_name.

    Resolvers::field_async

    fn Resolvers::field_async(self : Resolvers, type_name : String, field_name : String, resolver : async (ResolveInfo) ->
    Json
    ) -> Unit

    Register resolver for TypeName.fieldName as one that waits. The asynchronous engine prefers it over a synchronous registration of the same field; the synchronous engine cannot run it and uses the synchronous one.

    Resolvers::hook

    fn Resolvers::hook(self : Resolvers, extension : &Extension) -> Unit

    Register a hook (Extension): it sees every field before it is resolved, and the value after it is completed. Tracing, metrics and a field-level cache are extensions rather than branches inside the engine. extend itself is a keyword, so the registration is named after what it adds.

    Resolvers::is_type_of

    fn Resolvers::is_type_of(self : Resolvers, type_name : String, hook : (
    Json
    ,
    Json
    ) -> Bool) -> Unit

    Answer whether a value belongs to the object type type_name — the counterpart of graphql-core's is_type_of, asked of each possible type in turn when neither a resolve_type hook nor a __typename decided it.

    Resolvers::new

    fn Resolvers::new() -> Resolvers

    Create an empty resolver registry.

    Resolvers::permission

    fn Resolvers::permission(self : Resolvers, type_name : String, field_name : String, permission : (ResolveInfo) -> Bool) -> Unit

    Ask permission before the field is resolved. A field that answers false is refused with a not permitted field error, at the field's own path, and its resolver is never called.

    Resolvers::resolve_type

    fn Resolvers::resolve_type(self : Resolvers, type_name : String, hook : (
    Json
    ,
    Json
    ) -> String?) -> Unit

    Answer which object type a value at the interface or union type_name is. The hook receives the value and the request context and returns the object type's name, or None when it is not one this hook knows. It is asked before the value's own __typename, so a resolver that classifies values itself does not depend on the executor's convention.

    Sender

    What a producer holds while it feeds a stream. Dropping every sender ends the stream; close ends it at once.

    Sender::close

    fn Sender::close(self : Sender) -> Unit

    End the stream: a pending or later next answers None.

    Sender::send

    fn Sender::send(self : Sender, event :
    Json
    ) -> Unit raise

    Put an event into the stream. A closed stream refuses it, which is how a producer learns that nobody is listening any more.

    Stream

    pub struct Stream {
    pull : async () ->
    Json
    ?
    stop : () -> Unit
    }

    A live source of subscription events: something to pull from, and something to stop. next answers None when the source has ended, which is the signal a transport turns into the terminating complete.

    The pull is asynchronous because the events are not there yet — they arrive from a queue, a socket, or a database's change feed — and a subscription must not look like a list that was computed up front.

    Stream::channel

    fn Stream::channel() -> (Stream, Sender)

    A stream paired with the sender that feeds it. The queue is unbounded, so a producer never blocks on a slow subscriber and never loses an event.

    Stream::close

    fn Stream::close(self : Stream) -> Unit

    Stop the source. Called on the way out of a subscription: the client sent complete, the socket closed, or the last event had already arrived.

    Stream::next

    async fn Stream::next(self : Stream) ->
    Json
    ?

    The next event, or None when the source has ended. An error the source raises is the subscriber's problem to report: it is not a field error, because the payload it would belong to does not exist.

    Stream::of

    fn Stream::of(pull~ : async () ->
    Json
    ?, stop? : () -> Unit) -> Stream

    A stream over any pull, with a way to stop it. stop is called once, when the subscriber goes away or the stream ends.

    Subscribers

    pub struct Subscribers {
    map : Map[String, (ResolveInfo) -> Array[
    Json
    ] raise FieldError]
    streams : Map[String, (ResolveInfo) -> Stream raise FieldError]
    }

    A registry of subscription source resolvers, keyed by "TypeName.fieldName" on the subscription root type. A source returns the ordered stream of event payloads; each payload is the resolved value of the root field for one event, and its sub-selection is resolved against it like any object value.

    Subscribers::field

    fn Subscribers::field(self : Subscribers, type_name : String, field_name : String, source : (ResolveInfo) -> Array[
    Json
    ] raise FieldError) -> Unit

    Register a source stream for the root subscription field field_name on type_name. The source is called once per operation and yields the ordered events to deliver.

    Subscribers::new

    Create an empty subscription source registry.

    Subscribers::stream

    fn Subscribers::stream(self : Subscribers, type_name : String, field_name : String, source : (ResolveInfo) -> Stream raise FieldError) -> Unit

    Register a live source for the root subscription field field_name on type_name: one that hands back a Stream whose events arrive while the subscription is open. A source registered this way is preferred over a materialised one, so a schema can offer both — a list of events for a test, a real feed in production.

    SubscriptionResult

    pub(all) enum SubscriptionResult {
    RequestError(Array[
    GqlError
    ])
    EventStream(Array[
    Json
    ])
    }

    The outcome of establishing a subscription (GraphQL spec §6.2.3 CreateSourceEventStream + MapSourceToResponseEvent): either the request failed before any event could be produced — a parse, validation, or source-resolution error the client should receive instead of a stream — or the source yielded its ordered { data, errors } response payloads. Keeping the two apart lets a transport (the graphql-transport-ws handler) send a single error message for the former and a run of next messages for the latter, which the flat "one error response in the payload list" shape cannot distinguish.

    build_response

    Assemble the final { errors?, data?, extensions? } response object. data is omitted for request-level failures (no execution began) and included (possibly null) once execution starts; errors is omitted when empty, and so is the server's extensions map (§7.1, the entry reserved for whatever a server wants to say alongside the result — timings, a trace id, a cache hint).

    create_source_event_stream

    fn create_source_event_stream(schema :
    Schema
    , resolvers : Resolvers, subscribers : Subscribers, query : String, variables? : Map[String,
    Json
    ], operation_name? : String, root_value? :
    Json
    , context? :
    Json
    ) -> SubscriptionResult

    Establish a subscription and materialise its event payloads. Parses and validates query, requires a single-root-field subscription operation, invokes the registered source, and maps every event through execute_subscription_event. Any failure before the first event — parse/validation error, a non-subscription operation, more than one root field, an introspection root, a missing source, or a source FieldError — is a RequestError; success is an EventStream of ordered response payloads.

    execute

    fn execute(schema :
    Schema
    , resolvers : Resolvers, query : String, variables? : Map[String,
    Json
    ], operation_name? : String, root_value? :
    Json
    , context? :
    Json
    , extensions? : Map[String,
    Json
    ]) ->
    Json

    Execute a GraphQL request end to end: parse query, validate it against schema, select the operation, coerce variables, then walk the selection set with resolvers — returning the { data, errors } response as JSON.

    variables supplies operation variable values, operation_name picks an operation when the document has several, root_value seeds the root object, context is threaded to every resolver, and extensions is the server's own extensions entry on the response (§7.1), emitted when non-empty. The function never raises: parse and validation failures yield an errors-only response, and field errors are collected alongside partial data.

    execute_async

    async fn execute_async(schema :
    Schema
    , resolvers : Resolvers, query : String, variables? : Map[String,
    Json
    ], operation_name? : String, root_value? :
    Json
    , context? :
    Json
    , extensions? : Map[String,
    Json
    ], limits? : Limits) ->
    Json

    Execute a GraphQL request with resolvers that wait, answering one response.

    This is execute_incremental with incremental delivery switched off: @defer and @stream collapse to their in-line meanings, so the response is the whole result in one payload, shaped exactly like execute's.

    execute_incremental

    async fn execute_incremental(schema :
    Schema
    , resolvers : Resolvers, query : String, variables? : Map[String,
    Json
    ], operation_name? : String, root_value? :
    Json
    , context? :
    Json
    , extensions? : Map[String,
    Json
    ], limits? : Limits) -> Array[
    Json
    ]

    Execute a GraphQL request with resolvers that wait, delivering the response in pieces when the document defers or streams work.

    The payloads are, in order: the initial { data, hasNext }, then one per round of postponed work — each carrying incremental entries with the path and label the document asked for — and the last answers hasNext: false. A document that defers nothing produces exactly one payload.

    execute_subscription

    fn execute_subscription(schema :
    Schema
    , resolvers : Resolvers, subscribers : Subscribers, query : String, variables? : Map[String,
    Json
    ], operation_name? : String, root_value? :
    Json
    , context? :
    Json
    ) -> Array[
    Json
    ]

    Run a subscription operation and collect its ordered response payloads.

    A thin wrapper over create_source_event_stream: a RequestError collapses to a single { errors } response (the flat shape callers without a streaming transport expect), and an EventStream is returned as-is. variables, operation_name, root_value and context mirror execute.

    field_error

    fn field_error(e : Error) -> FieldError

    A resolver's error as a field error: a FieldError as it is, anything else by its text.

    limits

    let limits : Limits

    The preset: siblings resolved concurrently but not without bound, and enough room for the deferrals and streamed chunks a real document asks for.

    not_permitted

    fn not_permitted() -> FieldError

    Any error a resolver raised, as the field error it becomes; a FieldError passes through unchanged.

    operation_type

    Which kind of operation a request would run, chosen the way execute chooses it. The server needs this before running anything: a subscription goes over a socket, and GraphQL over HTTP refuses a mutation sent by GET.

    read

    fn[T : FromGql] read(value :
    Json
    , key : String) -> T raise FieldError

    The member key of an object value as any [FromGql] type, the way a generated input type reads its fields; a missing member reads as null. A refusal names the member.

    subscribe_live

    fn subscribe_live(schema :
    Schema
    , resolvers : Resolvers, subscribers : Subscribers, query : String, variables? : Map[String,
    Json
    ], operation_name? : String, root_value? :
    Json
    , context? :
    Json
    , limits? : Limits) -> Result[Live, Array[
    GqlError
    ]]

    Establish a subscription over a live source, ready to be pumped event by event. The left side is the request failure a client should receive instead of a stream; the right side is the stream of payloads.