# `Sigra.Install.Injector`
[🔗](https://github.com/sztheory/sigra/blob/v1.20.0/lib/sigra/install/injector.ex#L1)

Idempotent code injection for Sigra install generator.

This module handles injecting authentication-related code into
existing files in the host application (router, config, test support).
All injection functions check for a marker comment before injecting
to ensure idempotency.

# `apply`

```elixir
@spec apply(
  Sigra.Install.Injection.t(),
  keyword()
) :: {:ok, :injected | :already_present} | {:error, term()}
```

Applies a `%Sigra.Install.Injection{}` record, routing to the
appropriate marker-based injection function based on the anchor.

Returns `{:ok, :injected}` on first apply, `{:ok, :already_present}`
on subsequent applies (idempotency primitive behind GEN-04).

Features never call `Injector.inject_*` functions directly; they
return `%Injection{}` records from the `injections/1` callback in
`Sigra.Install.Feature` and the walker passes them here.

This is a thin adapter layer added for Phase 11 Wave 1 primitives.
The legacy `inject_router_plugs/2` / `inject_config/2` / ...
functions above continue to serve the monolith until Wave 4 swaps
the monolith for the walker.

# `inject_api_config`

```elixir
@spec inject_api_config(String.t(), String.t()) ::
  {:ok, String.t()} | {:already_injected, String.t()}
```

Injects API token configuration into config.exs.

Returns `{:ok, new_contents}` if injection succeeds, or
`{:already_injected, contents}` if the API marker is already present.

# `inject_api_routes`

```elixir
@spec inject_api_routes(String.t(), String.t()) ::
  {:ok, String.t()} | {:already_injected, String.t()}
```

Injects API pipeline and routes into the router file.

Adds an `:api_authenticated` pipeline with FetchBearer and
RequireAuthenticated plugs, plus API token CRUD routes.

Returns `{:ok, new_contents}` if injection succeeds, or
`{:already_injected, contents}` if the API marker is already present.

# `inject_app_js_passkeys`

```elixir
@spec inject_app_js_passkeys(String.t(), String.t()) ::
  {:ok, String.t()}
  | {:already_injected, String.t()}
  | {:manual_action, String.t()}
```

Injects the passkey hook import and merged hook registration into
`assets/js/app.js` when the standard Phoenix hook shape is present.

Marker detection is authoritative: once `// Sigra passkeys:start` exists,
the file is treated as already injected on re-runs.

# `inject_config`

```elixir
@spec inject_config(String.t(), String.t()) ::
  {:ok, String.t()} | {:already_injected, String.t()}
```

Injects Sigra configuration into config.exs.

Returns `{:ok, new_contents}` if injection succeeds, or
`{:already_injected, contents}` if the marker is already present.

# `inject_conn_case`

```elixir
@spec inject_conn_case(String.t(), String.t()) ::
  {:ok, String.t()} | {:already_injected, String.t()}
```

Injects auth test helper import into conn_case.ex.

Returns `{:ok, new_contents}` if injection succeeds, or
`{:already_injected, contents}` if the helper import is already present.

# `inject_jwt_routes`

```elixir
@spec inject_jwt_routes(String.t(), String.t()) ::
  {:ok, String.t()} | {:already_injected, String.t()}
```

Injects JWT authentication routes into the router file.

Adds unauthenticated `/api/auth` scope with token create, refresh,
MFA, and revoke endpoints. Only used with `--jwt` flag.

Returns `{:ok, new_contents}` if injection succeeds, or
`{:already_injected, contents}` if the JWT marker is already present.

# `inject_lifecycle_routes`

```elixir
@spec inject_lifecycle_routes(String.t(), String.t()) ::
  {:ok, String.t()} | {:already_injected, String.t()}
```

Injects account lifecycle routes into the router file.

Adds settings, email confirmation, and reactivation routes to the
authenticated scope. Also includes the auth_hooks.ex file in the
generator output list.

Routes injected:
- `live "/users/settings", SettingsLive, :index`
- `live "/users/settings/confirm-email/:token", SettingsLive, :confirm_email`
- `live "/users/reactivation", ReactivationLive, :index`

Returns `{:ok, new_contents}` if injection succeeds, or
`{:already_injected, contents}` if the lifecycle marker is already present.

# `inject_oauth_config`

```elixir
@spec inject_oauth_config(String.t(), String.t()) ::
  {:ok, String.t()} | {:already_injected, String.t()}
```

Injects OAuth provider configuration into config.exs.

Returns `{:ok, new_contents}` if injection succeeds, or
`{:already_injected, contents}` if the OAuth marker is already present.

# `inject_oauth_routes`

```elixir
@spec inject_oauth_routes(String.t(), String.t()) ::
  {:ok, String.t()} | {:already_injected, String.t()}
```

Injects OAuth routes into the router file.

Returns `{:ok, new_contents}` if injection succeeds, or
`{:already_injected, contents}` if the OAuth marker is already present.

# `inject_oban_lifecycle_queue`

```elixir
@spec inject_oban_lifecycle_queue(String.t()) ::
  {:ok, String.t()} | {:already_injected, String.t()}
```

Injects the `:sigra_lifecycle` queue into Oban configuration.

Adds the lifecycle queue alongside the existing mailer queue.

Returns `{:ok, new_contents}` if injection succeeds, or
`{:already_injected, contents}` if the queue is already present.

# `inject_router_plugs`

```elixir
@spec inject_router_plugs(String.t(), String.t()) ::
  {:ok, String.t()} | {:already_injected, String.t()}
```

Injects authentication pipeline and routes into the router file.

Returns `{:ok, new_contents}` if injection succeeds, or
`{:already_injected, contents}` if the marker is already present.

# `inject_test_config`

```elixir
@spec inject_test_config(String.t(), String.t()) ::
  {:ok, String.t()} | {:already_injected, String.t()}
```

Injects Argon2 test speedup configuration into test.exs.

Returns `{:ok, new_contents}` if injection succeeds, or
`{:already_injected, contents}` if the marker is already present.

# `inject_vault_child`

```elixir
@spec inject_vault_child(String.t(), String.t()) ::
  {:ok, String.t()} | {:already_injected, String.t()}
```

Injects Vault child spec into the application supervision tree.

Finds the `children = [` list in application.ex and adds
`{MyApp.Vault, []}` to it.

Returns `{:ok, new_contents}` if injection succeeds, or
`{:already_injected, contents}` if Vault is already present.

# `lifecycle_template_files`

```elixir
@spec lifecycle_template_files() :: [String.t()]
```

Returns the list of files that the generator should include for
account lifecycle features, including auth_hooks.ex.

---

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