# `Reactor.Builder`
[🔗](https://github.com/ash-project/reactor/blob/main/lib/reactor/builder.ex/#L6)

Build a new Reactor programmatically.

You don't _have_ to use the Reactor DSL to create a Reactor.  The functions in
this module allow you to define a Reactor programmatically.  This is
especially useful if you need to create a reactor dynamically (maybe based on
a UI such as [React Flow](https://reactflow.dev/)).

## Example

```elixir
reactor = Builder.new()
{:ok, reactor} = Builder.add_input(reactor, :name)
argument = Argument.from_input(:name)
{:ok, reactor} = Builder.add_step(reactor, :greet, [argument])
{:ok, reactor} = Builder.return(reactor, :greet)
```

# `arguments_transform`

```elixir
@type arguments_transform() ::
  {:transform,
   nil
   | (%{optional(atom()) =&gt; any()} -&gt; %{optional(atom()) =&gt; any()})
   | {module(), keyword()}
   | mfa()}
```

Optionally transform all the arguments into new arguments

# `async?`

```elixir
@type async?() :: {:async?, boolean() | (keyword() -&gt; boolean())}
```

Should the step be run asynchronously?

# `context`

```elixir
@type context() :: Reactor.context()
```

Optional context which will be merged with the reactor context when calling this step.

# `description`

```elixir
@type description() :: {:description, nil | String.t()}
```

An optional step description

# `guards`

```elixir
@type guards() :: {:guards, [Reactor.Guard.Build.t()]}
```

# `impl`

```elixir
@type impl() :: module() | {module(), keyword()}
```

# `max_retries`

```elixir
@type max_retries() :: {:max_retries, :infinity | non_neg_integer()}
```

How many times is the step allowed to retry?

# `ref`

```elixir
@type ref() :: {:ref, :step_name | :make_ref}
```

# `step_argument`

```elixir
@type step_argument() :: Reactor.Argument.t() | {atom(), {:input | :result, any()}}
```

# `step_options`

```elixir
@type step_options() :: [
  async?()
  | description()
  | guards()
  | max_retries()
  | arguments_transform()
  | context()
  | ref()
]
```

# `add_input`

```elixir
@spec add_input(Reactor.t(), any(), Reactor.Builder.Input.options()) ::
  {:ok, Reactor.t()} | {:error, any()}
```

Add a named input to the Reactor.

This both places the input in the Reactor for later input validation and adds
steps to the Reactor which will emit and (possibly) transform the input.

# `add_input!`

```elixir
@spec add_input!(Reactor.t(), any(), Reactor.Builder.Input.options()) ::
  Reactor.t() | no_return()
```

Raising version of `add_input/2..3`.

# `add_middleware`

```elixir
@spec add_middleware(Reactor.t(), Reactor.Middleware.t()) ::
  {:ok, Reactor.t()} | {:error, any()}
```

Add a middleware to the Reactor.

Returns an error if the middleware is already present on the Reactor.

# `add_middleware!`

```elixir
@spec add_middleware!(Reactor.t(), Reactor.Middleware.t()) ::
  Reactor.t() | no_return()
```

Raising version of `add_middleware/2`.

# `add_step`

```elixir
@spec add_step(Reactor.t(), name :: any(), impl(), [step_argument()], step_options()) ::
  {:ok, Reactor.t()} | {:error, any()}
```

Add a step to the Reactor.

Add a new step to the Reactor.  Rewrites input arguments to use the result of
the input steps and injects transformation steps as required.

# `add_step!`

```elixir
@spec add_step!(Reactor.t(), name :: any(), impl(), [step_argument()], step_options()) ::
  Reactor.t() | no_return()
```

Raising version of `add_step/3..5`.

# `compose`

```elixir
@spec compose(
  Reactor.t(),
  atom(),
  Reactor.t() | module(),
  [step_argument()],
  Keyword.t()
) ::
  {:ok, Reactor.t()} | {:error, any()}
```

Compose another Reactor inside this one.

Whenever possible this function will extract the steps from inner Reactor and
place them inside the parent Reactor.  In order to achieve this the composer
will rename the steps to ensure that there are no conflicts.

If you're attempting to create a recursive Reactor (ie compose a Reactor
within itself) then this will be detected and runtime composition will be used
instead.  See `Reactor.Step.Compose` for more details.

# `compose!`

```elixir
@spec compose!(
  Reactor.t(),
  atom(),
  Reactor.t() | module(),
  [step_argument()],
  Keyword.t()
) ::
  Reactor.t() | no_return()
```

Raising version of `compose/4`.

# `ensure_middleware`

```elixir
@spec ensure_middleware(Reactor.t(), Reactor.Middleware.t()) ::
  {:ok, Reactor.t()} | {:error, any()}
```

Ensure that a middleware is present on the Reactor.

# `ensure_middleware!`

```elixir
@spec ensure_middleware!(Reactor.t(), Reactor.Middleware.t()) ::
  Reactor.t() | no_return()
```

Raising version of `ensure_middleware/2`.

# `new`

```elixir
@spec new(any()) :: Reactor.t()
```

Build a new, empty Reactor.

Optionally an identifier for the Reactor. This is primarily used for recursive
composition tracking.

# `new_step`

```elixir
@spec new_step(any(), impl(), [step_argument()], step_options()) ::
  {:ok, Reactor.Step.t()} | {:error, any()}
```

Build a step which can be added to a reactor at runtime.

Note that the built step doesn't support transformations - you should add an
additional step to do the transformation needed (this is what `add_step/5`
does anyway).

# `new_step!`

```elixir
@spec new_step!(any(), impl(), [step_argument()], step_options()) ::
  Reactor.Step.t() | no_return()
```

Raising version of `new_step/2..4`.

# `recurse`

```elixir
@spec recurse(
  Reactor.t(),
  atom(),
  Reactor.t() | module(),
  [step_argument()],
  Keyword.t()
) ::
  {:ok, Reactor.t()} | {:error, any()}
```

Recurse a Reactor until an exit condition is met or maximum iterations are reached.

Recursion takes the output of one execution of the reactor and feeds it as input
to the next execution, continuing until either:
1. The exit_condition function returns true when applied to the latest result
2. The maximum number of iterations is reached
3. An error occurs during execution

This provides a powerful way to implement iterative algorithms where the 
processing continues until a convergence condition is met.

# `recurse!`

```elixir
@spec recurse!(
  Reactor.t(),
  atom(),
  Reactor.t() | module(),
  [step_argument()],
  Keyword.t()
) ::
  Reactor.t() | no_return()
```

Raising version of `recurse/4`.

# `return`

```elixir
@spec return(Reactor.t(), any()) :: {:ok, Reactor.t()} | {:error, any()}
```

Specify the return value of the Reactor.

The return value must be the name of a step.

# `return!`

```elixir
@spec return!(Reactor.t(), any()) :: Reactor.t() | no_return()
```

Raising version of `return/2`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
