# `PhoenixKit.ModuleRegistry`
[🔗](https://github.com/BeamLabEU/phoenix_kit/blob/v1.7.105/lib/phoenix_kit/module_registry.ex#L1)

Runtime registry of all PhoenixKit modules (internal and external).

External modules are auto-discovered from beam files via `PhoenixKit.ModuleDiscovery`.
Any dep that depends on `:phoenix_kit` and uses `use PhoenixKit.Module` is found
automatically — no config line needed.

## External Module Registration

External hex packages are auto-discovered. Just add the dep:

    {:phoenix_kit_hello_world, "~> 0.1.0"}

For explicit registration (backwards compatible):

    config :phoenix_kit, :modules, [PhoenixKitHelloWorld]

## Runtime Registration

    PhoenixKit.ModuleRegistry.register(MyModule)
    PhoenixKit.ModuleRegistry.unregister(MyModule)

## Query API

    ModuleRegistry.all_modules()           # All registered module atoms
    ModuleRegistry.enabled_modules()       # Only currently enabled
    ModuleRegistry.all_admin_tabs()        # Collect admin tabs from all modules
    ModuleRegistry.all_settings_tabs()     # Collect settings tabs
    ModuleRegistry.all_user_dashboard_tabs() # Collect user dashboard tabs
    ModuleRegistry.all_children()          # Collect supervisor child specs
    ModuleRegistry.all_permission_metadata() # Collect permission metadata
    ModuleRegistry.feature_enabled_checks()  # Build {mod, :enabled?} map
    ModuleRegistry.get_by_key("tickets")   # Find module by key

# `all_admin_tabs`

```elixir
@spec all_admin_tabs() :: [PhoenixKit.Dashboard.Tab.t()]
```

Collect all admin tabs from all registered modules.

Note: iterates all modules and calls `admin_tabs/0` on each call.
For cached access in rendering paths, use `PhoenixKit.Dashboard.Registry.get_admin_tabs/0`.

# `all_children`

```elixir
@spec all_children() :: [Supervisor.child_spec()]
```

Collect all supervisor child specs from all registered modules.

# `all_feature_keys`

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

Returns all feature module key strings from registered modules.

# `all_modules`

```elixir
@spec all_modules() :: [module()]
```

Returns all registered module atoms.

# `all_permission_metadata`

```elixir
@spec all_permission_metadata() :: [PhoenixKit.Module.permission_meta()]
```

Collect permission metadata from all registered modules.

# `all_route_modules`

```elixir
@spec all_route_modules() :: [module()]
```

Collect route modules from all registered modules.

# `all_settings_tabs`

```elixir
@spec all_settings_tabs() :: [PhoenixKit.Dashboard.Tab.t()]
```

Collect all settings tabs from all registered modules.

# `all_user_dashboard_tabs`

```elixir
@spec all_user_dashboard_tabs() :: [PhoenixKit.Dashboard.Tab.t()]
```

Collect all user dashboard tabs from all registered modules.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `dependency_warnings`

```elixir
@spec dependency_warnings() :: [map()]
```

Returns dependency warnings for the Modules page.

Each warning is a map:
  %{module: module(), module_name: String.t(), requires_key: String.t()}

Called on Modules page render — computes live (not hot path).

# `enabled_modules`

```elixir
@spec enabled_modules() :: [module()]
```

Returns all registered modules that are currently enabled.

# `feature_enabled_checks`

```elixir
@spec feature_enabled_checks() :: %{required(String.t()) =&gt; {module(), atom()}}
```

Build a feature_enabled_checks map from registered modules.

Returns `%{"referrals" => {PhoenixKit.Modules.Referrals, :enabled?}, ...}`

# `get_by_key`

```elixir
@spec get_by_key(String.t()) :: module() | nil
```

Find a registered module by its key string.

# `initialized?`

```elixir
@spec initialized?() :: boolean()
```

Check if the registry has been initialized.

# `modules_with_migrations`

```elixir
@spec modules_with_migrations() :: [{String.t(), module()}]
```

Collect modules that have versioned migrations.

Returns a list of `{module_name, migration_module}` tuples for all registered
modules that implement `migration_module/0` and return a non-nil value.

# `not_installed_packages`

```elixir
@spec not_installed_packages() :: [map()]
```

Returns known external PhoenixKit packages that are not currently installed.

Used by the admin Modules page to inform users about available packages
they can add as dependencies.

# `permission_descriptions`

```elixir
@spec permission_descriptions() :: %{required(String.t()) =&gt; String.t()}
```

Returns permission descriptions map from registered modules.

# `permission_icons`

```elixir
@spec permission_icons() :: %{required(String.t()) =&gt; String.t()}
```

Returns permission icons map from registered modules.

# `permission_labels`

```elixir
@spec permission_labels() :: %{required(String.t()) =&gt; String.t()}
```

Returns permission labels map from registered modules.

# `register`

```elixir
@spec register(module()) :: :ok
```

Register a module that implements PhoenixKit.Module behaviour.

# `run_all_legacy_migrations`

```elixir
@spec run_all_legacy_migrations() :: %{required(module()) =&gt; :ok | {:error, term()}}
```

Run every enabled module's `migrate_legacy/0` callback.

Iterates registered modules, calls `migrate_legacy/0` on each that
implements it, swallows per-module errors so the host-app boot can't
be taken down by a flaky migration. Each module's implementation is
expected to be idempotent (safe to re-run on every boot).

Activity logging happens inside each module's `migrate_legacy/0` —
this orchestrator only logs the per-module pass/fail outcome to the
Logger, not to Activity.

Designed to be called once from a host app's `Application.start/2`
after the Repo and supervision tree are up:

    def start(_type, _args) do
      children = [...]
      result = Supervisor.start_link(children, opts)
      PhoenixKit.ModuleRegistry.run_all_legacy_migrations()
      result
    end

Returns a summary map: `%{module_atom => :ok | {:error, term()}}`.

# `start_link`

# `static_children`

```elixir
@spec static_children() :: [Supervisor.child_spec()]
```

Collect supervisor child specs from the static module list.

This does NOT require the GenServer to be running, making it safe to call
from the PhoenixKit.Supervisor init (before the registry starts).

# `unregister`

```elixir
@spec unregister(module()) :: :ok
```

Unregister a module.

---

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