-module(mochi@batch). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/mochi/batch.gleam"). -export([default_config/0, with_max_batch_size/2, with_continue_on_error/2, with_parallel_execution/2, request/1, request_with_variables/2, request_with_operation/2, full_request/3, execute_with_operation_name/6, execute_batch/4, parse_batch_request/1, parse_batch_requests/1]). -export_type([batch_request/0, batch_config/0, batch_result/0]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. -type batch_request() :: {batch_request, binary(), gleam@option:option(gleam@dict:dict(binary(), gleam@dynamic:dynamic_())), gleam@option:option(binary())}. -type batch_config() :: {batch_config, integer(), boolean(), boolean()}. -type batch_result() :: {batch_result, list(mochi@executor:execution_result()), boolean(), integer()}. -file("src/mochi/batch.gleam", 66). ?DOC(" Create default batch configuration\n"). -spec default_config() -> batch_config(). default_config() -> {batch_config, 10, true, false}. -file("src/mochi/batch.gleam", 75). ?DOC(" Set maximum batch size\n"). -spec with_max_batch_size(batch_config(), integer()) -> batch_config(). with_max_batch_size(Config, Size) -> {batch_config, Size, erlang:element(3, Config), erlang:element(4, Config)}. -file("src/mochi/batch.gleam", 80). ?DOC(" Configure whether to continue on error\n"). -spec with_continue_on_error(batch_config(), boolean()) -> batch_config(). with_continue_on_error(Config, Continue) -> {batch_config, erlang:element(2, Config), Continue, erlang:element(4, Config)}. -file("src/mochi/batch.gleam", 88). ?DOC(" Configure parallel execution\n"). -spec with_parallel_execution(batch_config(), boolean()) -> batch_config(). with_parallel_execution(Config, Parallel) -> {batch_config, erlang:element(2, Config), erlang:element(3, Config), Parallel}. -file("src/mochi/batch.gleam", 100). ?DOC(" Create a batch request with just a query\n"). -spec request(binary()) -> batch_request(). request(Query) -> {batch_request, Query, none, none}. -file("src/mochi/batch.gleam", 105). ?DOC(" Create a batch request with variables\n"). -spec request_with_variables( binary(), gleam@dict:dict(binary(), gleam@dynamic:dynamic_()) ) -> batch_request(). request_with_variables(Query, Variables) -> {batch_request, Query, {some, Variables}, none}. -file("src/mochi/batch.gleam", 113). ?DOC(" Create a batch request with operation name\n"). -spec request_with_operation(binary(), binary()) -> batch_request(). request_with_operation(Query, Operation_name) -> {batch_request, Query, none, {some, Operation_name}}. -file("src/mochi/batch.gleam", 125). ?DOC(" Create a full batch request\n"). -spec full_request( binary(), gleam@dict:dict(binary(), gleam@dynamic:dynamic_()), binary() ) -> batch_request(). full_request(Query, Variables, Operation_name) -> {batch_request, Query, {some, Variables}, {some, Operation_name}}. -file("src/mochi/batch.gleam", 207). ?DOC(" Get the name of an operation\n"). -spec get_operation_name(mochi@internal@ast:operation()) -> gleam@option:option(binary()). get_operation_name(Operation) -> case Operation of {operation, _, Name, _, _, _} -> Name; {shorthand_query, _} -> none end. -file("src/mochi/batch.gleam", 175). ?DOC(" Find an operation in a document by name\n"). -spec find_operation( mochi@internal@ast:document(), gleam@option:option(binary()) ) -> {ok, mochi@internal@ast:operation()} | {error, binary()}. find_operation(Document, Operation_name) -> Operations = begin _pipe = erlang:element(2, Document), gleam@list:filter_map(_pipe, fun(Def) -> case Def of {operation_definition, Op} -> {ok, Op}; {fragment_definition, _} -> {error, nil} end end) end, case {Operation_name, Operations} of {none, [Single]} -> {ok, Single}; {none, []} -> {error, <<"Document contains no operations"/utf8>>}; {none, _} -> {error, <<"Document contains multiple operations, operation name is required"/utf8>>}; {{some, Name}, Ops} -> _pipe@1 = Ops, _pipe@2 = gleam@list:find( _pipe@1, fun(Op@1) -> get_operation_name(Op@1) =:= {some, Name} end ), gleam@result:map_error( _pipe@2, fun(_) -> <<<<"Operation '"/utf8, Name/binary>>/binary, "' not found in document"/utf8>> end ) end. -file("src/mochi/batch.gleam", 215). ?DOC(" Extract all fragment definitions from a document\n"). -spec extract_fragments(mochi@internal@ast:document()) -> list(mochi@internal@ast:fragment()). extract_fragments(Document) -> _pipe = erlang:element(2, Document), gleam@list:filter_map(_pipe, fun(Def) -> case Def of {fragment_definition, Fragment} -> {ok, Fragment}; {operation_definition, _} -> {error, nil} end end). -file("src/mochi/batch.gleam", 143). ?DOC( " Execute a query with a specific operation name selected\n" " This is the core function for operation selection in batched queries\n" ). -spec execute_with_operation_name( mochi@schema:schema(), mochi@internal@ast:document(), gleam@option:option(gleam@dynamic:dynamic_()), mochi@schema:execution_context(), gleam@dict:dict(binary(), gleam@dynamic:dynamic_()), gleam@option:option(binary()) ) -> mochi@executor:execution_result(). execute_with_operation_name( Schema_def, Document, Root_value, Ctx, Variables, Operation_name ) -> case find_operation(Document, Operation_name) of {ok, Operation} -> Fragments = extract_fragments(Document), New_doc = {document, [{operation_definition, Operation} | gleam@list:map( Fragments, fun(F) -> {fragment_definition, F} end )]}, mochi@executor:execute( Schema_def, New_doc, Root_value, Ctx, Variables ); {error, Msg} -> {execution_result, none, [{validation_error, Msg, [], none}], []} end. -file("src/mochi/batch.gleam", 355). -spec has_errors(mochi@executor:execution_result()) -> boolean(). has_errors(Result) -> not gleam@list:is_empty(erlang:element(3, Result)). -file("src/mochi/batch.gleam", 359). -spec format_parse_error(mochi@parser:parse_error()) -> binary(). format_parse_error(Error) -> case Error of {lex_error, _} -> <<"Lexer error"/utf8>>; {unexpected_token, Expected, _, _} -> <<"Expected "/utf8, Expected/binary>>; {unexpected_e_o_f, Expected@1} -> <<"Unexpected EOF, expected "/utf8, Expected@1/binary>> end. -file("src/mochi/batch.gleam", 323). -spec execute_single_request( mochi@schema:schema(), batch_request(), mochi@schema:execution_context() ) -> mochi@executor:execution_result(). execute_single_request(Schema_def, Req, Ctx) -> Variables = gleam@option:unwrap(erlang:element(3, Req), maps:new()), case mochi@executor:get_or_parse(Schema_def, erlang:element(2, Req)) of {ok, Document} -> execute_with_operation_name( Schema_def, Document, none, Ctx, Variables, erlang:element(4, Req) ); {error, Parse_error} -> {execution_result, none, [{validation_error, <<"Parse error: "/utf8, (format_parse_error(Parse_error))/binary>>, [], none}], []} end. -file("src/mochi/batch.gleam", 261). -spec execute_batch_internal( mochi@schema:schema(), list(batch_request()), batch_config(), mochi@schema:execution_context() ) -> batch_result(). execute_batch_internal(Schema_def, Requests, Config, Ctx) -> Final_results = case erlang:element(4, Config) of true -> _pipe = Requests, _pipe@1 = gleam@list:map( _pipe, fun(Req) -> fun() -> execute_single_request(Schema_def, Req, Ctx) end end ), mochi_parallel_ffi:parallel_map(_pipe@1); false -> _pipe@2 = gleam@list:fold( Requests, {[], false}, fun(Acc, Req@1) -> {Results_acc, Has_failure} = Acc, case {erlang:element(3, Config), Has_failure} of {false, true} -> {[{execution_result, none, [{validation_error, <<"Batch execution halted due to error"/utf8>>, [], none}], []} | Results_acc], true}; {_, _} -> Result = execute_single_request( Schema_def, Req@1, Ctx ), New_has_failure = Has_failure orelse has_errors( Result ), {[Result | Results_acc], New_has_failure} end end ), (fun(Pair) -> lists:reverse(erlang:element(1, Pair)) end)(_pipe@2) end, Failure_count = begin _pipe@3 = Final_results, gleam@list:count(_pipe@3, fun has_errors/1) end, {batch_result, Final_results, Failure_count =:= 0, Failure_count}. -file("src/mochi/batch.gleam", 230). ?DOC(" Execute a batch of requests\n"). -spec execute_batch( mochi@schema:schema(), list(batch_request()), batch_config(), mochi@schema:execution_context() ) -> batch_result(). execute_batch(Schema_def, Requests, Config, Ctx) -> case erlang:length(Requests) > erlang:element(2, Config) of true -> {batch_result, [{execution_result, none, [{validation_error, <<"Batch size exceeds maximum of "/utf8, (gleam@string:inspect( erlang:element(2, Config) ))/binary>>, [], none}], []}], false, 1}; false -> execute_batch_internal(Schema_def, Requests, Config, Ctx) end. -file("src/mochi/batch.gleam", 447). -spec get_list_raw(gleam@dynamic:dynamic_()) -> {ok, list(gleam@dynamic:dynamic_())} | {error, nil}. get_list_raw(Value) -> _pipe = gleam@dynamic@decode:run( Value, gleam@dynamic@decode:list( {decoder, fun gleam@dynamic@decode:decode_dynamic/1} ) ), gleam@result:map_error(_pipe, fun(_) -> nil end). -file("src/mochi/batch.gleam", 452). -spec extract_string_field(gleam@dynamic:dynamic_(), binary()) -> {ok, binary()} | {error, nil}. extract_string_field(Value, Field) -> _pipe = gleam@dynamic@decode:run( Value, gleam@dynamic@decode:at( [Field], {decoder, fun gleam@dynamic@decode:decode_string/1} ) ), gleam@result:map_error(_pipe, fun(_) -> nil end). -file("src/mochi/batch.gleam", 426). -spec extract_query(gleam@dynamic:dynamic_()) -> {ok, binary()} | {error, binary()}. extract_query(Value) -> case extract_string_field(Value, <<"query"/utf8>>) of {ok, Q} -> {ok, Q}; {error, _} -> {error, <<"Missing or invalid 'query' field"/utf8>>} end. -file("src/mochi/batch.gleam", 440). -spec extract_operation_name(gleam@dynamic:dynamic_()) -> gleam@option:option(binary()). extract_operation_name(Value) -> case extract_string_field(Value, <<"operationName"/utf8>>) of {ok, Name} -> {some, Name}; {error, _} -> none end. -file("src/mochi/batch.gleam", 457). -spec extract_dict_field(gleam@dynamic:dynamic_(), binary()) -> {ok, gleam@dict:dict(binary(), gleam@dynamic:dynamic_())} | {error, nil}. extract_dict_field(Value, Field) -> _pipe = gleam@dynamic@decode:run( Value, gleam@dynamic@decode:at( [Field], gleam@dynamic@decode:dict( {decoder, fun gleam@dynamic@decode:decode_string/1}, {decoder, fun gleam@dynamic@decode:decode_dynamic/1} ) ) ), gleam@result:map_error(_pipe, fun(_) -> nil end). -file("src/mochi/batch.gleam", 433). -spec extract_variables(gleam@dynamic:dynamic_()) -> gleam@option:option(gleam@dict:dict(binary(), gleam@dynamic:dynamic_())). extract_variables(Value) -> case extract_dict_field(Value, <<"variables"/utf8>>) of {ok, Vars} -> {some, Vars}; {error, _} -> none end. -file("src/mochi/batch.gleam", 372). ?DOC(" Parse a batch request from a dynamic value (e.g., from JSON)\n"). -spec parse_batch_request(gleam@dynamic:dynamic_()) -> {ok, batch_request()} | {error, binary()}. parse_batch_request(Value) -> case extract_query(Value) of {ok, Query} -> Variables = extract_variables(Value), Operation_name = extract_operation_name(Value), {ok, {batch_request, Query, Variables, Operation_name}}; {error, E} -> {error, E} end. -file("src/mochi/batch.gleam", 388). ?DOC(" Parse multiple batch requests from a dynamic list\n"). -spec parse_batch_requests(gleam@dynamic:dynamic_()) -> {ok, list(batch_request())} | {error, binary()}. parse_batch_requests(Value) -> case get_list_raw(Value) of {ok, Items} -> Results = gleam@list:map(Items, fun parse_batch_request/1), Errors = begin _pipe = Results, gleam@list:filter_map(_pipe, fun(R) -> case R of {error, E} -> {ok, E}; {ok, _} -> {error, nil} end end) end, case Errors of [] -> {ok, gleam@list:filter_map(Results, fun(R@1) -> case R@1 of {ok, Req} -> {ok, Req}; {error, _} -> {error, nil} end end)}; [First | _] -> {error, <<"Failed to parse batch request: "/utf8, First/binary>>} end; {error, _} -> case parse_batch_request(Value) of {ok, Req@1} -> {ok, [Req@1]}; {error, E@1} -> {error, E@1} end end.