-module(aion@child). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/aion/child.gleam"). -export([spawn/6, error_codec/1, output_codec/1, child_id/1, await/1, spawn_and_wait/6]). -export_type([child_handle/2]). -if(?OTP_RELEASE >= 27). -define(MODULEDOC(Str), -moduledoc(Str)). -define(DOC(Str), -doc(Str)). -else. -define(MODULEDOC(Str), -compile([])). -define(DOC(Str), -compile([])). -endif. ?MODULEDOC(" Typed child-workflow handles and await wrappers.\n"). -opaque child_handle(EQU, EQV) :: {child_handle, binary(), aion@codec:codec(EQU), aion@codec:codec(EQV)}. -file("src/aion/child.gleam", 169). -spec spawn_config() -> binary(). spawn_config() -> _pipe = gleam@json:object([]), gleam@json:to_string(_pipe). -file("src/aion/child.gleam", 30). ?DOC( " Start a linked child workflow and return its typed handle.\n" "\n" " The `workflow_fn` is accepted as a type anchor for the child workflow's\n" " `fn(input) -> Result(output, workflow_error)` contract. The SDK does not call\n" " it here; lifecycle, linking, recording, and replay/no-respawn behavior are\n" " owned by AT/AD behind the FFI boundary.\n" ). -spec spawn( binary(), fun((EQW) -> {ok, EQX} | {error, EQY}), EQW, aion@codec:codec(EQW), aion@codec:codec(EQX), aion@codec:codec(EQY) ) -> {ok, child_handle(EQX, EQY)} | {error, aion@error:engine_error()}. spawn(Name, Workflow_fn, Input, Input_codec, Output_codec, Error_codec) -> _ = Workflow_fn, Encoded_input = (erlang:element(2, Input_codec))(Input), case aion_flow_ffi:spawn_child(Name, Encoded_input, spawn_config()) of {ok, Raw_child_id} -> {ok, {child_handle, Raw_child_id, Output_codec, Error_codec}}; {error, Raw_error} -> {error, {engine_failure, Raw_error}} end. -file("src/aion/child.gleam", 135). -spec copy_payload_suffix(binary(), integer()) -> binary(). copy_payload_suffix(Raw_result, Prefix_size) -> _pipe = Raw_result, _pipe@1 = gleam@string:drop_start(_pipe, Prefix_size), (fun(Suffix) -> erlang:iolist_to_binary([Suffix]) end)(_pipe@1). -file("src/aion/child.gleam", 115). ?DOC(" Return the workflow-error codec carried by this child handle.\n"). -spec error_codec(child_handle(any(), ESK)) -> aion@codec:codec(ESK). error_codec(Handle) -> erlang:element(4, Handle). -file("src/aion/child.gleam", 158). -spec decode_error_payload(binary(), child_handle(ETD, ETE)) -> {ok, ETD} | {error, aion@error:child_error(ETE)}. decode_error_payload(Payload, Handle) -> Codec = error_codec(Handle), case (erlang:element(3, Codec))(Payload) of {ok, Workflow_error} -> {error, {child_workflow_failed, Workflow_error}}; {error, Decode_error} -> {error, {child_error_decode_failed, Decode_error}} end. -file("src/aion/child.gleam", 108). ?DOC(" Return the output codec carried by this child handle.\n"). -spec output_codec(child_handle(ESE, any())) -> aion@codec:codec(ESE). output_codec(Handle) -> erlang:element(3, Handle). -file("src/aion/child.gleam", 147). -spec decode_output(binary(), child_handle(ESW, ESX)) -> {ok, ESW} | {error, aion@error:child_error(ESX)}. decode_output(Payload, Handle) -> Codec = output_codec(Handle), case (erlang:element(3, Codec))(Payload) of {ok, Output} -> {ok, Output}; {error, Decode_error} -> {error, {child_output_decode_failed, Decode_error}} end. -file("src/aion/child.gleam", 121). -spec decode_child_result(binary(), child_handle(ESO, ESP)) -> {ok, ESO} | {error, aion@error:child_error(ESP)}. decode_child_result(Raw_result, Handle) -> case gleam_stdlib:string_starts_with(Raw_result, <<"ok:"/utf8>>) of true -> decode_output(copy_payload_suffix(Raw_result, 3), Handle); false -> case gleam_stdlib:string_starts_with(Raw_result, <<"error:"/utf8>>) of true -> decode_error_payload( copy_payload_suffix(Raw_result, 6), Handle ); false -> {error, {child_engine_failure, Raw_result}} end end. -file("src/aion/child.gleam", 103). ?DOC(" Return the engine child/correlation id carried by this handle.\n"). -spec child_id(child_handle(any(), any())) -> binary(). child_id(Handle) -> erlang:element(2, Handle). -file("src/aion/child.gleam", 63). ?DOC( " Await a child workflow's recorded completion or failure.\n" "\n" " AT/AD own blocking, replay resolution, and event recording. This wrapper\n" " decodes the raw recorded envelope with the codecs carried on the handle and\n" " returns decode/engine failures as typed data.\n" "\n" " The await is a yield point: pending workflow queries are serviced by the\n" " query pump before the child terminal resolves, exactly as activity awaits,\n" " signal receives, and timers do. Without the pump, a query arriving while\n" " the workflow is parked here would surface its sentinel as a bogus child\n" " failure and leave the engine refusing every later await in the run.\n" ). -spec await(child_handle(ERI, ERJ)) -> {ok, ERI} | {error, aion@error:child_error(ERJ)}. await(Handle) -> Awaited_child_id = child_id(Handle), case aion@internal@pump:run( fun() -> aion@internal@pump:shield( aion_flow_ffi:await_child(Awaited_child_id) ) end ) of {ok, Raw_result} -> decode_child_result(Raw_result, Handle); {error, Raw_error} -> {error, {child_engine_failure, Raw_error}} end. -file("src/aion/child.gleam", 87). ?DOC( " Start a linked child workflow and await its recorded result.\n" "\n" " This is the spawn-then-await convenience kept in the child logic module so\n" " `aion/workflow` can remain a forwarding authoring surface.\n" ). -spec spawn_and_wait( binary(), fun((ERP) -> {ok, ERQ} | {error, ERR}), ERP, aion@codec:codec(ERP), aion@codec:codec(ERQ), aion@codec:codec(ERR) ) -> {ok, ERQ} | {error, aion@error:child_error(ERR)}. spawn_and_wait(Name, Workflow_fn, Input, Input_codec, Output_codec, Error_codec) -> case spawn(Name, Workflow_fn, Input, Input_codec, Output_codec, Error_codec) of {ok, Handle} -> await(Handle); {error, {engine_failure, Message}} -> {error, {child_engine_failure, Message}} end.