A JSON-compatible fan-out declaration.
When the collection is resolved
collection decides more than which list is read — it decides when the
expansion happens, and so which of two mechanisms runs it:
- rooted at
$input.or$context.— a static fan-out. Both roots are populated before anything runs, soBaton.Flow.Compilerexpands the node into one job per item inside the workflow's insert transaction. - rooted at
$steps.— a dynamic fan-out. The collection is an upstream node's result, so its size is unknown until that node finishes. The node compiles to a single expander step that creates the rest at run time (Baton.Expansion).
Everything downstream is identical either way: one job per item, carrying
$item, and a reader that declares a dep on the logical node reads the
ordered expansion results as $steps.<node_id>.
max_items caps the expansion. It is a guard against a producer that
degenerates — the failure mode dynamic fan-out exists to avoid, arriving
from the other direction — and applies to both kinds: a static fan-out fails
to compile, a dynamic one discards its expander rather than inserting
thousands of jobs.
Summary
Functions
The default expansion cap applied when a spec does not set one.
Whether this fan-out is resolved at run time from an upstream result rather than at compile time from the run's input or context.
The step name for one expansion, or why the item cannot have one.
The gate as the atom Baton.Expansion and Baton.Flow.FanOut use.
Types
@type t() :: %Baton.Flow.FanOutSpec{ collection: String.t(), gate: String.t(), item_id: String.t(), max_items: pos_integer() }
Functions
@spec default_max_items() :: pos_integer()
The default expansion cap applied when a spec does not set one.
Whether this fan-out is resolved at run time from an upstream result rather than at compile time from the run's input or context.
The step name for one expansion, or why the item cannot have one.
Both expanders build the name the same way — the node id, an underscore, and
the resolved item_id — and both have the same two ways to fail, so the rule
lives here rather than once per path. A suffix that is neither a string nor a
number cannot name anything; one that pushes the name past what step_name
can hold (Baton.Node.step_name_limit/0) would be refused by the database,
and a name refused there surfaces as a raised error from inside the
expansion transaction rather than a reason anyone can act on.
Both are permanent properties of the item, so both are for the caller to discard on: the static path cannot compile, and the dynamic path cannot make a model's output shorter by asking again.
@spec gate_atom(t()) :: :parallel | :sequential
The gate as the atom Baton.Expansion and Baton.Flow.FanOut use.