# `PhoenixKitEntities.Mirror.Storage`
[🔗](https://github.com/BeamLabEU/phoenix_kit_entities/blob/v0.4.6/lib/phoenix_kit_entities/mirror/storage.ex#L1)

Filesystem storage operations for entity mirror/export system.

Stores exported JSON files in the parent app's priv/entities/ directory.
Each entity is stored as a single file containing both the definition and all data records.

## Directory Structure

    priv/entities/
      brand.json       # Contains definition + all data records
      product.json     # Contains definition + all data records

## File Format

    {
      "export_version": "1.0",
      "exported_at": "2025-12-11T10:30:00Z",
      "definition": { ... entity schema ... },
      "data": [ ... array of data records ... ]
    }

## Configuration

The export path can be configured via settings:
- `entities_mirror_path` - Custom path (empty = use default priv/entities/)

# `data_enabled?`

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

Checks if entity data mirroring is enabled.

# `default_path`

```elixir
@spec default_path() :: String.t()
```

Returns the default storage path in the parent app's priv directory.

# `definitions_enabled?`

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

Checks if entity definitions mirroring is enabled.

# `delete_entity`

```elixir
@spec delete_entity(String.t()) :: :ok | {:error, term()}
```

Deletes an entity file.

# `disable_data`

```elixir
@spec disable_data() :: {:ok, any()} | {:error, any()}
```

Disables entity data mirroring.

# `disable_definitions`

```elixir
@spec disable_definitions() :: {:ok, any()} | {:error, any()}
```

Disables entity definitions mirroring.

# `enable_data`

```elixir
@spec enable_data() :: {:ok, any()} | {:error, any()}
```

Enables entity data mirroring.

# `enable_definitions`

```elixir
@spec enable_definitions() :: {:ok, any()} | {:error, any()}
```

Enables entity definitions mirroring.

# `ensure_directory`

```elixir
@spec ensure_directory() :: :ok | {:error, term()}
```

Ensures the root directory exists.

# `entity_exists?`

```elixir
@spec entity_exists?(String.t()) :: boolean()
```

Checks if a file exists for the given entity.

# `entity_path`

```elixir
@spec entity_path(String.t()) :: String.t()
```

Returns the file path for a specific entity.

# `get_stats`

```elixir
@spec get_stats() :: map()
```

Returns statistics about exported files.

## Returns
  Map with:
  - `definitions_count` - Number of exported entity files
  - `data_count` - Total number of data records across all entities
  - `entities_with_data` - List of entity names that have data records
  - `last_export` - Timestamp of most recent export (nil if no files)

# `list_entities`

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

Lists all exported entity names.

Returns a list of entity names (without .json extension).

# `read_entity`

```elixir
@spec read_entity(String.t()) :: {:ok, map()} | {:error, term()}
```

Reads an entity file containing definition and data.

## Parameters
  - `entity_name` - The entity name

## Returns
  - `{:ok, map}` with decoded JSON on success
  - `{:error, reason}` on failure

# `root_path`

```elixir
@spec root_path() :: String.t()
```

Returns the root path for entity mirror storage.

Uses custom path from settings if configured, otherwise defaults to
the parent app's priv/entities/ directory.

The configured path is expanded via `Path.expand/1` and validated
against the parent app's root so an admin-edited setting can't
escape to write entity exports under arbitrary filesystem locations
(e.g. `../../etc`, `/tmp/foo`). Out-of-bounds paths fall back to
the safe default.

# `write_entity`

```elixir
@spec write_entity(String.t(), map()) :: {:ok, String.t()} | {:error, term()}
```

Writes an entity file containing definition and optionally data.

## Parameters
  - `entity_name` - The entity name (used as filename)
  - `content` - The full content map with definition and data

## Returns
  - `{:ok, file_path}` on success
  - `{:error, reason}` on failure

---

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