# `MishkaInstaller.Event.Event`
[🔗](https://github.com/mishka-group/mishka_installer/blob/v0.1.14/lib/event/event.ex#L1)

The `MishkaInstaller.Event.Event` module is the core of events, which comprises pre-prepared
strategies for the implementation and management of plugins introduced to the system
around various events.
These implementation and management strategies are included in the Event module.

> #### Use cases information {: .tip}
>
> The fundamental criteria of the same system serve as the basis for the specification of
> these strategies, which are aimed at common systems.
> Therefore, you are not permitted to utilize this module if you believe that
> your technique is different.

In addition, it is important to remember that the other part of this module is
connected to the queries that are required to be made to the **Erlang runtime database**,
which is known as `Mnesia`.

Using this functionality, you will be able to store the information that is
associated with each plugin in it.

> Note that all storages are based on the name of an event. See `MishkaInstaller.Event.Hook` module.

## Statuses and dependencies

A plugin record carries one of `:registered`, `:started`, `:restarted`, `:stopped` and `:held`.
Only the last two are meaningful decisions: `:stopped` is an operator's switch and outlives
restarts and dependency changes, `:held` means "one of my dependencies is not active" and is
managed by the library.

Each lifecycle function keeps that distinction:

- `stop/3` disables a plugin — from any status, including `:held` — and holds everything that
  depends on it, transitively and across events (`sync_dependents/2`).
- `enable/2` is the only way out of `:stopped`. `restart/3` and `start/3` refuse it, so no reload
  or reset path can silently undo the operator.
- `start/3`, `restart/3` and `enable/2` release the plugins that were held waiting for them, and a
  plugin whose dependencies are still inactive is stored as `:held` rather than run.
- `unregister/3` counts as an unsatisfied dependency for whatever depended on the removed plugin.

Every one of these recompiles the events that changed, so the compiled chain and the stored
statuses never disagree, and status writes are durable (see `write/3`).

---

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.
>
> This module is a read-only in-memory storage optimized for the fastest possible read times
> not for write strategies.

### Note:

When you are writing, you should always make an effort to be more careful because
you might get reconditioned during times of high traffic.
When it comes to reading and running all plugins, this problem only occurs when a
module is being created and destroyed during the compilation process.

# `builder_entry`

```elixir
@type builder_entry() :: {:root, struct() | map(), :edit} | struct() | map()
```

# `error_return`

```elixir
@type error_return() ::
  {:error, [%{action: atom(), field: atom(), message: String.t()}]}
```

# `okey_return`

```elixir
@type okey_return() :: {:ok, struct() | map() | module() | [any()]}
```

# `status`

```elixir
@type status() :: :registered | :started | :stopped | :restarted | :held
```

# `t`

```elixir
@type t() :: %MishkaInstaller.Event.Event{
  depends: [String.t()] | nil,
  event: String.t(),
  extension: atom(),
  extra: [map()] | nil,
  id: MishkaInstaller.Helper.UUID.t() | nil,
  inserted_at: DateTime.t() | nil,
  name: module(),
  priority: integer() | nil,
  status: status() | nil,
  updated_at: DateTime.t() | nil
}
```

# `builder`

# `connections`

```elixir
@spec connections() :: Graph.t()
```

The live plugin dependency graph as a [`libgraph`](https://hexdocs.pm/libgraph) `Graph` (one
`plugin -> dependency` edge per `:depends` entry). Useful for inspecting plugin connections, e.g.
`Graph.to_dot/1` or `Graph.topsort/1`.

# `delete`

```elixir
@spec delete(atom(), String.t() | module()) :: error_return() | okey_return()
```

To delete a plugin from Mnesia database by id or name.

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.

## Example:

```elixir
delete(:name, TestApp.User.Auth)

delete(:id, "c63aea42-209a-40fb-b5c6-a0d28ee7e25b")
```

# `dependency_order`

```elixir
@spec dependency_order([struct()]) :: [struct()]
```

Orders `plugins` (each with `:name`, `:priority`, `:depends`) so a plugin runs **after** the
plugins it depends on, breaking ties by `{priority, name}`. The set is assumed acyclic (cycles are
rejected at `register/3`); on a stray cycle it falls back to priority order.

# `dependents`

```elixir
@spec dependents(module()) :: [struct()]
```

Every plugin that declares `name` in its `:depends`, i.e. the reverse dependency edge. Dependencies
are between **plugins**, so this is global: a dependent registered on another event is included.

## Example:

```elixir
dependents(TestApp.User.Auth)
```

# `dirty_get`

```elixir
@spec dirty_get(:name, module()) :: map() | struct() | nil
```

Like `get(:name, name)` but a **dirty** (non-transactional) index read — a direct ETS lookup with
no lock or commit overhead. Use only where a slightly stale read is fine (e.g. the background plugin
status sync), never where you need transactional consistency.

# `drop`

```elixir
@spec drop() :: {:ok, :atomic} | {:error, any(), charlist()}
```

To drop all plugins from Mnesia database.

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.

## Example:

```elixir
drop()
```

# `enable`

```elixir
@spec enable(module(), boolean()) :: error_return() | okey_return()
```

Re-enables a plugin an operator disabled with `stop/3`. This is the **only** transition out of
`:stopped`, so no reload, reset or restart path can silently undo a user's decision to switch a
plugin off.

If the plugin's dependencies are not active, it is stored as `:held` instead of `:restarted` and
released automatically as soon as they come back — the call still succeeds, since the operator's
intent ("this plugin should run") is recorded either way.

## Example:

```elixir
stop(:name, TestApp.User.Auth, true)
enable(TestApp.User.Auth)
```

# `enforce_keys`

# `enforce_keys`

# `example`

A sample %MishkaInstaller.Event.Event{} populated from defaults + type-based fallbacks.

# `get`

```elixir
@spec get() :: [map() | struct()]
```

To get all plugins information from Mnesia database.

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.

## Example:

```elixir
get()
```

# `get`

```elixir
@spec get(String.t()) :: map() | struct() | nil
```

To get a plugin information from Mnesia database by id.

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.

## Example:

```elixir
get("c63aea42-209a-40fb-b5c6-a0d28ee7e25b")
```

# `get`

```elixir
@spec get(:name | :event | :extension, module() | String.t()) ::
  [map() | struct()] | map() | struct() | nil
```

To get all plugins or one plugin information from Mnesia database.

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.

## Example:

```elixir
# All plugins of an event
get(:event, "after_login")

# All plugins of an extension
get(:extension, :mishka_developer_tools)

# Get a plugin
get(:name, TestApp.User.Auth)
```

# `group_events`

```elixir
@spec group_events([atom()]) :: error_return() | okey_return()
```

To get all the events defined in the Mnesia database (Only unique items are returned).

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.

## Example:

```elixir
group_events()
```

# `ids`

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

To get all plugins ids from Mnesia database.

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.

## Example:

```elixir
ids()
```

# `keys`

# `keys`

# `no_dependency_cycle`

```elixir
@spec no_dependency_cycle(module(), list()) :: :ok | error_return()
```

Returns `:ok` when `depends` introduces no dependency cycle back to `name`, otherwise an error.
Used by `register/3` to reject plugins that would otherwise stay `:held` forever (e.g. `A → B → A`).
Built on [`libgraph`](https://hexdocs.pm/libgraph): the candidate's edges are added to the live
dependency graph and `Graph.is_acyclic?/1` decides.

# `register`

```elixir
@spec register(module(), String.t(), map()) :: error_return() | okey_return()
```

If you want to store a plugin for a particular event, this function is a predefined strategy
that you can use. Additionally, it performs a check on the integrity of the plugin module and
gathers other information that is necessary while it is being stored in `Mnesia`.

Inputs for this function include the **name of the plugin**, **the name of the event**, and some
**fundamental information**.
Checking the `struct` of this module is one way to add the `initial event` to the modules.

See: `%MishkaInstaller.Event.Event{}`

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.
>
> This module is a read-only in-memory storage optimized for the fastest possible read times
> not for write strategies.
>
> It is not appropriate for the user to have any direct influence on this function because
> it is intended for the use of the programmer. It should be brought to your attention that
> this particular function, which incorporates the `Write` activity, should often be utilized
> only once at the beginning of the project.

## Example:

```elixir
register(TestApp.User.Auth, "after_login", %{priority: 2})
```

> This function is only responsible for registering the plugin; it does not carry
> out any activities related to execution. Please refer to function `start/2` in order to execute

### Re-registering an existing plugin

This function is an **upsert keyed on the plugin name**: registering an already-registered plugin
never creates a second row, it reconciles the existing one. The fields declared in code
(`:priority`, `:extra`, `:depends`) always win, so changing them in a new version of your plugin
reaches installs that registered with the old values; the runtime-owned fields (`:id`, `:status`,
`:inserted_at`) are preserved, so a plugin an operator disabled stays disabled across upgrades.

A changed `:depends` is re-checked for cycles and the status is re-evaluated: the plugin moves to
`:held` when a new dependency is inactive, and back out of `:held` when its dependencies are
satisfied.

# `restart`

```elixir
@spec restart(boolean()) :: okey_return() | error_return()
```

This function restarts all events, For more information please see `restart/2`.

# `restart`

```elixir
@spec restart(:name | :event, module() | String.t(), boolean()) ::
  error_return() | okey_return()
```

A pre-made strategy for restarting a plugin is what this method is clearly named.
Keep in mind that you'll need to register and start this plugin ahead of time.

The requested Event will be compiled once more in the event that the
plugin is able to restart properly. **This is a heavy write activity**

> #### Use cases information {: .tip}
>
> The `start/2` function and this idea are often mistaken. Actually, this function does
> the same operations as the start function, but it re-examines the conditions.
> You can tailor your own function to meet the specific needs of your system, and there's
> already a plan in place. The `MishkaInstaller.Event.Hook` module decides on this potential.

---

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.
>
> This module is a read-only in-memory storage optimized for the fastest possible read times
> not for write strategies.
>
> It is not appropriate for the user to have any direct influence on this function because
> it is intended for the use of the programmer. It should be brought to your attention that
> this particular function, which incorporates the `Write` activity, should often be utilized
> only once at the beginning of the project.

##### Compile path: `MishkaInstaller.Event.ModuleStateCompiler.State.YourEvent`.

## Example:

```elixir
# Restart a plugin
restart(:name, TestApp.User.Auth)

# Restart all plugins of an event
restart(:event, "after_login")
```

# `start`

```elixir
@spec start(boolean()) :: okey_return() | error_return()
```

This function starts all events, For more information please see `start/2`.

# `start`

```elixir
@spec start(:name | :event, module() | String.t(), boolean()) ::
  error_return() | okey_return()
```

A pre-made strategy for starting a plugin is what this method is clearly named.
Keep in mind that you'll need to register this plugin ahead of time.

The requested Event will be compiled once more in the event that the
plugin is able to start properly. **This is a heavy write activity**

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.
>
> This module is a read-only in-memory storage optimized for the fastest possible read times
> not for write strategies.
>
> It is not appropriate for the user to have any direct influence on this function because
> it is intended for the use of the programmer. It should be brought to your attention that
> this particular function, which incorporates the `Write` activity, should often be utilized
> only once at the beginning of the project.

##### Compile path: `MishkaInstaller.Event.ModuleStateCompiler.State.YourEvent`.

## Example:

```elixir
# Start a plugin
start(:name, TestApp.User.Auth)

# Start all plugins of an event
start(:event, "after_login")
```

# `stop`

```elixir
@spec stop(boolean()) :: okey_return() | error_return()
```

This function stops all events, For more information please see `stop/2`.

# `stop`

```elixir
@spec stop(:name | :event, module() | String.t(), boolean()) ::
  okey_return() | error_return()
```

A pre-made strategy for stopping a plugin is what this method is clearly named.
Keep in mind that you'll need to register and start this plugin ahead of time.

The requested Event will be compiled once more in the event that the
plugin is able to stop properly. **This is a heavy write activity**

> #### Use cases information {: .tip}
>
> Keep in mind that the only thing that happens when you stop a plugin is that its database status
> changes to `stopped` and it is removed from the list of compiled modules
> of the event in question. However, its modules, notably `GenServer`,
> will still be operational and will not be entirely removed from the system.

---

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.
>
> This module is a read-only in-memory storage optimized for the fastest possible read times
> not for write strategies.
>
> It is not appropriate for the user to have any direct influence on this function because
> it is intended for the use of the programmer. It should be brought to your attention that
> this particular function, which incorporates the `Write` activity, should often be utilized
> only once at the beginning of the project.

## Example:

```elixir
# Stop a plugin
stop(:name, TestApp.User.Auth)

# Stop all plugins of an event
stop(:event, "after_login")
```

# `sync_dependents`

```elixir
@spec sync_dependents(module() | [module()], boolean()) :: [String.t()]
```

Re-evaluates everything that depends on `names` after their status changed, and recompiles each
affected event exactly once. Called by `start/3`, `restart/3`, `stop/3`, `enable/2` and
`unregister/3`; you only need it directly if you write statuses yourself with `write/3`.

A dependent whose dependencies are no longer all active is written as `:held`; a `:held` dependent
whose dependencies are satisfied again is written as `:restarted`. The walk is transitive, so a
chain `A -> B -> C` (C depends on B, B depends on A) holds and releases as a unit, and it is
cycle-safe: `names` are never re-evaluated and each dependent is visited once per settling round.

Two statuses are never touched: `:stopped`, which is an operator decision and must outlive any
dependency change, and `:registered`, which belongs to a plugin that has not been started yet.

Returns the list of events that were recompiled.

## Example:

```elixir
write(:name, TestApp.User.Auth, %{status: :stopped})
sync_dependents(TestApp.User.Auth)
```

# `unique`

To check is a plugin unique or not in Mnesia database.

> It returns `:ok`, or `{:error, reason}`. Note that if the requested plugin does not exist,
> it means it is unique, and if it is already in the database, it means it is not unique

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.

## Example:

```elixir
unique(:name, TestApp.User.Auth)
```

# `unique?`

This function is exactly like `unique/2` function, except that its output is a Boolean.

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.

## Example:

```elixir
unique?(:name, TestApp.User.Auth)
```

# `unregister`

```elixir
@spec unregister(boolean()) :: okey_return() | error_return()
```

This function stops all unregisters, For more information please see `unregister/2`.

# `unregister`

```elixir
@spec unregister(:name | :event, module() | String.t(), boolean()) ::
  okey_return() | error_return()
```

This function removes the plugin from the database and **kills** all processes associated to it,
in addition to doing the exact same thing as the `stop/2` function.

The requested Event will be compiled once more in the event that the
plugin is able to unregister properly. **This is a heavy write activity**

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.
>
> This module is a read-only in-memory storage optimized for the fastest possible read times
> not for write strategies.
>
> It is not appropriate for the user to have any direct influence on this function because
> it is intended for the use of the programmer. It should be brought to your attention that
> this particular function, which incorporates the `Write` activity, should often be utilized
> only once at the beginning of the project.

## Example:

```elixir
# Unregister a plugin
unregister(:name, TestApp.User.Auth)

# Unregister all plugins of an event
unregister(:event, "after_login")
```

# `unregister_gone`

```elixir
@spec unregister_gone(boolean()) :: [struct()]
```

Unregisters every plugin whose code is gone — the application that registered it (its
`:extension`) is loaded and holds no such module — through `unregister/3`, so whatever depended
on it is held, and returns the records it removed. A plugin whose application is not loaded is
left as it is: that extension may still be on its way in, and its event runs none of its plugins
until the application is loaded or the plugin is unregistered.

`MishkaInstaller.Event.EventHandler` runs this once on boot, after the run-time extensions are
loaded and before `start/1`. Without it, a plugin removed or renamed in a newer version of its
extension stays registered, and its event compiles to a stub that runs none of its plugins:
`Hook.call/3` on it answers its `cannot_run` option, or the state it is given. Run it by hand
after loading a new version of an extension without a restart. **This is a heavy write activity**

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.
>
> This module is a read-only in-memory storage optimized for the fastest possible read times
> not for write strategies.
>
> It is not appropriate for the user to have any direct influence on this function because
> it is intended for the use of the programmer.

## Example:

```elixir
unregister_gone()
```

# `write`

```elixir
@spec write(builder_entry()) :: error_return() | okey_return()
```

To Add or edit a plugin information from Mnesia database.

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.

## Example:

```elixir
write(%MishkaInstaller.Event.Event{name: TestApp.User.Auth, event: "after_login", extension: :test_app})
```

# `write`

```elixir
@spec write(atom(), String.t() | module(), map()) :: error_return() | okey_return()
```

To edit a specific field/fields of a plugin from the Mnesia database.

> The first input can only be name and ID `[:id, :name]`.

The record is re-read inside the transaction under a write lock and `updated_to` is merged onto
the **current** row, so two concurrent transitions of the same plugin cannot clobber each other's
fields. The transaction is synchronous (see
`MishkaInstaller.Helper.MnesiaAssistant.Transaction.durable_transaction/1`), so a status change is on
disk before this function returns and survives an unclean shutdown.

> #### Security considerations {: .warning}
>
> It is important to remember that all of the functionalities contained within this
> section must be implemented at the **high access level**, and they should not directly take
> any input from the user. Ensure that you include the required safety measures.

## Example:

```elixir
write(:name, TestApp.User.Auth, %{status: :started})
```

---

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