# CompileLog Module Reference **Module:** `CompileLog` **File:** `lib/compile_log.ex` **Database:** `data/log.sqlite` **Process Name:** `:compile_log_db` --- ## Table of Contents - [Overview](#overview) - [Features](#features) - [Dependencies](#dependencies) - [Database Schema](#database-schema) - [Functions](#functions) - [setup_database/1](#setup_database1) - [log/2](#log2) - [get_recent/2](#get_recent2) - [get_by_file/2](#get_by_file2) - [get_git_diff/1](#get_git_diff1) - [cleanup_old_logs/2](#cleanup_old_logs2) - [stats/1](#stats1) - [Examples](#examples) - [Integration](#integration) - [Performance](#performance) - [Troubleshooting](#troubleshooting) --- ## Overview `CompileLog` is an automatic compilation logging system that captures Elixir file compilations with git diff information. It integrates seamlessly with `AutoCompile` to create a persistent audit trail of all code changes during development. ### Purpose - Track compilation events automatically - Capture git diffs at moment of compilation - Provide queryable history of file changes - Enable development workflow analysis - Support debugging and code review ### Automatic Operation Once configured, CompileLog operates transparently: 1. Developer saves an `.ex` file 2. `AutoCompile` compiles the file 3. `CompileLog.log/2` is called automatically 4. Git diff is captured and stored 5. Entry is persisted to SQLite database ### Storage All logs are stored in `data/log.sqlite` with indexed access for fast queries. --- ## Features
### ✅ Automatic Logging Triggered on every `.ex` file compilation without manual intervention. ### ✅ Git Diff Capture Stores `git diff -U0` output with minimal context for compact storage. ### ✅ Millisecond Precision Timestamps accurate to the millisecond for precise chronological ordering. ### ✅ File History Query complete compilation history for any file. ### ✅ Statistics Aggregate data showing most-compiled files and development patterns. ### ✅ Cleanup Utilities Remove old logs based on age to manage database size. ### ✅ SQLite Storage Persistent, indexed storage with efficient queries. ### ✅ Zero Configuration Works automatically once installed, no manual setup required.
--- ## Dependencies ### Required Modules | Module | Purpose | Used For | |--------|---------|----------| | `Sqler` | SQLite database wrapper | All database operations | | `Logger` | Elixir logging | Debug and error messages | | `System` | System commands | Git diff execution, timestamps | ### External Dependencies | Dependency | Required | Purpose | Fallback | |-----------|----------|---------|----------| | Git | Optional | Capture git diff | Returns `nil` if unavailable | | SQLite | Required | Database storage | Application won't start without it | ### Application Configuration Configured in `lib/mcp/application.ex`: ```elixir children = [ {Sqler, name: "log", register: :compile_log_db}, {AutoCompile, ["lib/"]}, # ... ] # In start/2 callback CompileLog.setup_database(:compile_log_db) ``` --- ## Database Schema ### Table: auto_compile Complete schema definition: ```sql CREATE TABLE IF NOT EXISTS auto_compile ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, time INTEGER NOT NULL, git_diff TEXT, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL ); CREATE INDEX IF NOT EXISTS idx_auto_compile_name ON auto_compile(name); CREATE INDEX IF NOT EXISTS idx_auto_compile_time ON auto_compile(time); ``` ### Column Definitions | Column | Type | Constraints | Description | |--------|------|-------------|-------------| | `id` | INTEGER | PRIMARY KEY AUTOINCREMENT | Auto-generated unique identifier | | `name` | TEXT | NOT NULL | File path relative to project root | | `time` | INTEGER | NOT NULL | Unix timestamp in milliseconds | | `git_diff` | TEXT | NULL | Output of `git diff -U0 ` | | `created_at` | INTEGER | NOT NULL | Record creation timestamp | | `updated_at` | INTEGER | NOT NULL | Record update timestamp (for Sqler) | ### Indexes **idx_auto_compile_name** - Column: `name` - Purpose: Fast file-specific queries - Used by: `get_by_file/2`, stats aggregation **idx_auto_compile_time** - Column: `time` - Purpose: Fast chronological queries - Used by: `get_recent/2`, time range queries ### Database File ``` Location: data/log.sqlite Created: Automatically on first startup Size: ~1KB per log entry (varies with diff size) ``` --- ## Functions ### setup_database/1
#### Description Initializes the database schema by creating the `auto_compile` table and indexes if they don't exist. #### Spec ```elixir @spec setup_database(atom()) :: :ok ``` #### Parameters **db** :: `atom()` (default: `:compile_log_db`) - Database process name - Must be a registered Sqler process - Default uses the standard compile log database #### Returns **:ok** :: Always returns success - Tables and indexes are created with `IF NOT EXISTS` - Safe to call multiple times - Logs "CompileLog database initialized" on completion #### Examples ```elixir # Default database CompileLog.setup_database() #=> :ok # Custom database CompileLog.setup_database(:my_log_db) #=> :ok # Called automatically on app startup # No need to call manually ``` #### Implementation Details Executes three SQL statements: 1. Creates `auto_compile` table 2. Creates `idx_auto_compile_name` index 3. Creates `idx_auto_compile_time` index Uses `Sqler.sql/2` for execution.
--- ### log/2
#### Description Logs a compilation event by capturing the current timestamp, getting git diff, and inserting a record into the database. #### Spec ```elixir @spec log(String.t(), atom()) :: {:ok, integer()} | {:error, any()} ``` #### Parameters **file_path** :: `String.t()` (required) - Path to the compiled file - Should be relative to project root - Example: `"lib/user.ex"` **db** :: `atom()` (default: `:compile_log_db`) - Database process name - Must be a registered Sqler process #### Returns **{:ok, log_id}** :: Success - `log_id` is the auto-generated database ID - ID is a millisecond timestamp (monotonically increasing) **{:error, reason}** :: Failure - Database insert failed - Returns error reason from Sqler #### Examples ```elixir # Basic usage (called automatically by AutoCompile) CompileLog.log("lib/user.ex") #=> {:ok, 1738350123456} # Manual logging with custom database CompileLog.log("lib/my_module.ex", :custom_db) #=> {:ok, 1738350234567} # Error case CompileLog.log("invalid/path.ex") #=> {:error, "Insert failed - no rows affected"} ``` #### Implementation ```elixir def log(file_path, db \\ @db_name) do now = System.system_time(:millisecond) git_diff = get_git_diff(file_path) log_entry = %{ name: file_path, time: now, git_diff: git_diff, created_at: now } # Sqler auto-generates id and updated_at Sqler.insert(db, "auto_compile", log_entry) end ``` #### Side Effects - Logs debug message on success - Logs error message on failure - Executes `git diff -U0 ` command
--- ### get_recent/2
#### Description Retrieves the most recent compilation logs, ordered by timestamp (newest first). #### Spec ```elixir @spec get_recent(non_neg_integer(), atom()) :: list() ``` #### Parameters **limit** :: `non_neg_integer()` (default: `20`) - Maximum number of logs to retrieve - Must be non-negative - No upper bound (use reasonable values) **db** :: `atom()` (default: `:compile_log_db`) - Database process name #### Returns **list()** :: List of log entries - Each entry: `[name, time]` - Ordered by `time DESC` (newest first) - Empty list if no logs exist **Note:** Currently returns simplified format. Full format would be `[id, name, time, git_diff, created_at, updated_at]`. #### Examples ```elixir # Get last 10 compilations CompileLog.get_recent(10) #=> [ # ["lib/user.ex", 1738350123456], # ["lib/compile_log.ex", 1738350098765], # ["lib/user.ex", 1738350045678] # ] # Get last 5 CompileLog.get_recent(5) #=> [...] # No compilations yet CompileLog.get_recent(10) #=> [] # Custom database CompileLog.get_recent(20, :my_db) #=> [...] ``` #### SQL Query ```sql SELECT name, time FROM auto_compile ORDER BY time DESC LIMIT ? ``` #### Performance - Uses `idx_auto_compile_time` index - O(log n) for ordering - Very fast even with thousands of entries
--- ### get_by_file/2
#### Description Retrieves compilation history for a specific file, ordered by timestamp (newest first). #### Spec ```elixir @spec get_by_file(String.t(), keyword()) :: list() ``` #### Parameters **file_path** :: `String.t()` (required) - Path to the file - Should match the path used in `log/2` - Example: `"lib/user.ex"` **opts** :: `keyword()` (default: `[]`) - Options keyword list #### Options **:limit** :: `integer()` (default: `10`) - Maximum logs to return **:db** :: `atom()` (default: `:compile_log_db`) - Database process name #### Returns **list()** :: List of log entries - Each entry: `[id, name, time, git_diff, created_at, updated_at]` - Ordered by `time DESC` - Empty list if file has no logs or on error #### Examples ```elixir # Get last 5 compilations of user.ex CompileLog.get_by_file("lib/user.ex", limit: 5) #=> [ # [1738350123456, "lib/user.ex", 1738350123456, "diff...", 1738350123456, 1738350123456], # [1738350098765, "lib/user.ex", 1738350098765, "", 1738350098765, 1738350098765], # ... # ] # Default limit (10) CompileLog.get_by_file("lib/my_module.ex") #=> [...] # With custom database CompileLog.get_by_file("lib/test.ex", limit: 3, db: :my_db) #=> [...] # File never compiled CompileLog.get_by_file("lib/new_file.ex") #=> [] ``` #### SQL Query ```sql SELECT id, name, time, git_diff, created_at, updated_at FROM auto_compile WHERE name = ? ORDER BY time DESC LIMIT ? ``` #### Performance - Uses `idx_auto_compile_name` index - O(log n) for lookup - Fast even with many compilations of same file #### Use Cases - View file edit history - Review changes during debugging - Track development focus - Code review preparation
--- ### get_git_diff/1
#### Description Executes `git diff -U0 ` to capture the current git diff for a file with minimal context. #### Spec ```elixir @spec get_git_diff(String.t()) :: String.t() | nil ``` #### Parameters **file_path** :: `String.t()` (required) - Path to the file - Relative to git repository root - Example: `"lib/user.ex"` #### Returns **String.t()** :: Git diff output - May be empty string if no changes - Contains unified diff format - Uses `-U0` flag (zero context lines) **nil** :: Git command failed - Not a git repository - File not tracked by git - System.cmd error - Git not installed #### Examples ```elixir # File with uncommitted changes CompileLog.get_git_diff("lib/user.ex") #=> """ # diff --git a/lib/user.ex b/lib/user.ex # index 1234567..abcdefg 100644 # --- a/lib/user.ex # +++ b/lib/user.ex # @@ -45 +45 @@ defmodule User do # - @session_duration_seconds 86400 # + @session_duration_seconds 43200 # """ # File with no changes (clean) CompileLog.get_git_diff("lib/user.ex") #=> "" # Not a git repository CompileLog.get_git_diff("lib/test.ex") #=> nil # File doesn't exist CompileLog.get_git_diff("lib/missing.ex") #=> nil ``` #### Git Command ```bash git diff -U0 ``` **Flags:** - `-U0` - Show zero lines of context around changes - Minimal output for compact storage - Only changed lines are shown #### Error Handling ```elixir def get_git_diff(file_path) do case System.cmd("git", ["diff", "-U0", file_path], stderr_to_stdout: true) do {output, 0} -> output {_output, _exit_code} -> nil end rescue _error -> nil end ``` Gracefully handles: - Non-git directories - Missing files - Git command errors - System.cmd exceptions #### Performance - Typical execution: 5-10ms - Depends on file size - No database access
--- ### cleanup_old_logs/2
#### Description Deletes compilation logs older than a specified number of days. #### Spec ```elixir @spec cleanup_old_logs(non_neg_integer(), atom()) :: {:ok, non_neg_integer()} | {:error, any()} ``` #### Parameters **days** :: `non_neg_integer()` (default: `30`) - Delete logs older than this many days - Based on `time` column - Calculated from current system time **db** :: `atom()` (default: `:compile_log_db`) - Database process name #### Returns **{:ok, count}** :: Success - `count` is number of deleted entries - May be 0 if no old logs exist **{:error, reason}** :: Failure - Database delete failed - Returns error reason #### Examples ```elixir # Delete logs older than 30 days (default) CompileLog.cleanup_old_logs() #=> {:ok, 15} # Deleted 15 entries # Weekly cleanup CompileLog.cleanup_old_logs(7) #=> {:ok, 42} # Daily cleanup CompileLog.cleanup_old_logs(1) #=> {:ok, 150} # No old logs CompileLog.cleanup_old_logs(30) #=> {:ok, 0} # Custom database CompileLog.cleanup_old_logs(30, :my_db) #=> {:ok, 5} ``` #### Time Calculation ```elixir cutoff_time = System.system_time(:millisecond) - days * 24 * 60 * 60 * 1000 ``` - Current time in milliseconds - Subtract days converted to milliseconds - All logs with `time < cutoff_time` are deleted #### SQL Executed ```sql DELETE FROM auto_compile WHERE time < ? -- Then retrieves count: SELECT changes() ``` #### Use Cases - Regular database maintenance - Manage storage space - Comply with data retention policies - Remove stale development logs #### Best Practices ```elixir # Monthly cleanup task defmodule MyApp.Scheduler do def monthly_cleanup do CompileLog.cleanup_old_logs(30) end end # Check size before/after before_size = File.stat!("data/log.sqlite").size {:ok, deleted} = CompileLog.cleanup_old_logs(30) after_size = File.stat!("data/log.sqlite").size saved = before_size - after_size IO.puts("Deleted #{deleted} logs, saved #{saved} bytes") ```
--- ### stats/1
#### Description Aggregates compilation data to provide development statistics and insights. #### Spec ```elixir @spec stats(atom()) :: map() ``` #### Parameters **db** :: `atom()` (default: `:compile_log_db`) - Database process name #### Returns **map()** :: Statistics map with the following keys: | Key | Type | Description | |-----|------|-------------| | `:total_compilations` | `integer()` | Total number of compilation logs | | `:unique_files` | `integer()` | Number of distinct files compiled | | `:most_compiled_file` | `{String.t(), integer()}` or `{nil, 0}` | File with most compilations and its count | | `:first_compilation` | `integer()` or `nil` | Timestamp of earliest compilation | | `:last_compilation` | `integer()` or `nil` | Timestamp of most recent compilation | #### Examples ```elixir # Normal case with data CompileLog.stats() #=> %{ # total_compilations: 150, # unique_files: 23, # most_compiled_file: {"lib/user.ex", 42}, # first_compilation: 1738265123456, # last_compilation: 1738350123456 # } # No compilations yet CompileLog.stats() #=> %{ # total_compilations: 0, # unique_files: 0, # most_compiled_file: {nil, 0}, # first_compilation: nil, # last_compilation: nil # } # Analyze development patterns stats = CompileLog.stats() {file, count} = stats.most_compiled_file avg = div(stats.total_compilations, max(stats.unique_files, 1)) IO.puts(""" Development Statistics: Total compilations: #{stats.total_compilations} Unique files: #{stats.unique_files} Average per file: #{avg} Hot spot: #{file} (#{count} times) """) ``` #### SQL Queries ```sql -- Total compilations SELECT COUNT(*) FROM auto_compile -- Unique files SELECT COUNT(DISTINCT name) FROM auto_compile -- Most compiled file SELECT name, COUNT(*) as count FROM auto_compile GROUP BY name ORDER BY count DESC LIMIT 1 -- Time range SELECT MIN(time), MAX(time) FROM auto_compile ``` #### Error Handling Uses `with` pattern for graceful error handling: ```elixir with {:ok, [[total]]} <- Sqler.sql(db, total_query), {:ok, [[unique]]} <- Sqler.sql(db, unique_query), # ... do %{total_compilations: total, ...} else _ -> %{total_compilations: 0, ...} end ``` Returns zeroed stats if any query fails. #### Use Cases - Identify frequently modified files - Track development activity patterns - Find potential refactoring candidates - Monitor project health - Generate development reports #### Performance - Executes 4 SQL queries - Uses aggregation functions - Typically completes in 1-5ms - Scales well with indexed columns
--- ## Examples ### Basic Logging ```elixir # Automatic logging (via AutoCompile) # 1. Save lib/user.ex # 2. AutoCompile compiles it # 3. CompileLog.log("lib/user.ex") is called # 4. Entry is stored with git diff # Manual logging CompileLog.log("lib/my_module.ex") #=> {:ok, 1738350123456} ``` ### Query Recent Activity ```elixir # Get last 10 compilations logs = CompileLog.get_recent(10) # Format and display Enum.each(logs, fn [name, time] -> dt = DateTime.from_unix!(time, :millisecond) formatted = Calendar.strftime(dt, "%H:%M:%S") IO.puts("#{formatted} - #{name}") end) # Output: # 14:23:45 - lib/user.ex # 14:22:10 - lib/compile_log.ex # 14:20:33 - lib/user.ex ``` ### Track File History ```elixir # Get compilation history history = CompileLog.get_by_file("lib/user.ex", limit: 5) IO.puts("Compilation history for lib/user.ex:") Enum.each(history, fn [_id, _name, time, diff, _, _] -> dt = DateTime.from_unix!(time, :millisecond) formatted = Calendar.strftime(dt, "%Y-%m-%d %H:%M:%S") diff_preview = String.slice(diff || "", 0..50) IO.puts(" #{formatted}") IO.puts(" #{diff_preview}...") end) ``` ### Development Analytics ```elixir # Get statistics stats = CompileLog.stats() # Calculate averages avg_per_file = if stats.unique_files > 0 do div(stats.total_compilations, stats.unique_files) else 0 end # Display report IO.puts(""" === Development Report === Activity: Total compilations: #{stats.total_compilations} Unique files: #{stats.unique_files} Average per file: #{avg_per_file} """) # Show hot spot case stats.most_compiled_file do {file, count} when not is_nil(file) -> percentage = round(count / stats.total_compilations * 100) IO.puts(""" Hot Spot: File: #{file} Compilations: #{count} Percentage: #{percentage}% """) if percentage > 30 do IO.puts(" ⚠️ Consider refactoring - high edit frequency") end {nil, 0} -> IO.puts("Hot Spot: None yet") end # Time range if stats.first_compilation do first = DateTime.from_unix!(stats.first_compilation, :millisecond) last = DateTime.from_unix!(stats.last_compilation, :millisecond) duration_ms = stats.last_compilation - stats.first_compilation duration_hours = div(duration_ms, 1000 * 60 * 60) IO.puts(""" Time Range: First: #{Calendar.strftime(first, "%Y-%m-%d %H:%M:%S")} Last: #{Calendar.strftime(last, "%Y-%m-%d %H:%M:%S")} Duration: #{duration_hours} hours """) end ``` ### Code Review Helper ```elixir # Review all changes in last hour hour_ago = System.system_time(:millisecond) - 3_600_000 recent = CompileLog.get_recent(50) recent_hour = Enum.filter(recent, fn [_name, time] -> time > hour_ago end) IO.puts("Changes in last hour: #{length(recent_hour)} compilations\n") # Group by file by_file = Enum.group_by(recent_hour, fn [name, _] -> name end) Enum.each(by_file, fn {file, compilations} -> IO.puts("#{file} (#{length(compilations)} times)") # Show latest diff [[_, last_time]] = Enum.take(compilations, 1) [[_, _, _, diff, _, _]] = CompileLog.get_by_file(file, limit: 1) if diff && diff != "" do IO.puts(diff) else IO.puts(" (no diff)") end IO.puts("") end) ``` ### Maintenance Tasks ```elixir # Regular cleanup defmodule CompileLogMaintenance do def weekly_cleanup do IO.puts("Running weekly compile log cleanup...") before_size = File.stat!("data/log.sqlite").size {:ok, deleted} = CompileLog.cleanup_old_logs(7) after_size = File.stat!("data/log.sqlite").size saved_kb = div(before_size - after_size, 1024) IO.puts(""" Cleanup complete: Deleted: #{deleted} logs Space saved: #{saved_kb} KB New size: #{div(after_size, 1024)} KB """) end def monthly_report do stats = CompileLog.stats() # Generate report report = """ === Monthly Development Report === Activity: #{stats.total_compilations} compilations Files: #{stats.unique_files} unique files Top File: #{elem(stats.most_compiled_file, 0)} (#{elem(stats.most_compiled_file, 1)} times) """ # Save to file filename = "reports/compile_log_#{Date.utc_today()}.txt" File.write!(filename, report) IO.puts("Report saved to #{filename}") end end ``` --- ## Integration ### With AutoCompile Automatic integration via `lib/auto_compile.ex`: ```elixir defp handle_ex_change(path) do case compile_file(path) do {:ok, module} -> # Log compilation automatically CompileLog.log(path) test_run(module) {:error, error} -> Logger.error("Compilation failed: #{inspect(error)}") end :ok end ``` ### Application Startup Initialization in `lib/mcp/application.ex`: ```elixir def start(_type, _args) do children = [ {Sqler, name: "log", register: :compile_log_db}, {AutoCompile, ["lib/"]}, # ... ] # ... case Supervisor.start_link(children, opts) do {:ok, pid} -> CompileLog.setup_database(:compile_log_db) {:ok, pid} error -> error end end ``` ### Custom Integration ```elixir # Manual compilation + logging defmodule MyCompiler do def compile_and_log(file_path) do # Compile result = Code.compile_file(file_path) # Log CompileLog.log(file_path) result end end ``` --- ## Performance ### Logging Overhead | Operation | Time | Notes | |-----------|------|-------| | Git diff | ~5ms | Depends on file size | | Database insert | ~1-2ms | SQLite write | | Total overhead | ~5-10ms | Per compilation | ### Query Performance | Query | Time | Notes | |-------|------|-------| | get_recent/2 | <1ms | Indexed by time | | get_by_file/2 | <1ms | Indexed by name | | stats/1 | 1-5ms | Multiple aggregations | | cleanup_old_logs/2 | 5-20ms | Depends on number deleted | ### Storage | Item | Size | Notes | |------|------|-------| | Empty diff entry | ~200 bytes | No git changes | | Average entry | ~500 bytes - 1KB | Typical diff | | Large diff entry | Up to several KB | Major changes | | Database overhead | ~10% | SQLite metadata | ### Optimization Tips ```elixir # Regular cleanup CompileLog.cleanup_old_logs(30) # Monitor database size File.stat!("data/log.sqlite").size # Use VACUUM to reclaim space (SQLite command) Sqler.sql(:compile_log_db, "VACUUM") ``` --- ## Troubleshooting ### No Logs Appearing ```elixir # 1. Check database process case Process.whereis(:compile_log_db) do nil -> IO.puts("❌ Database not running") pid -> IO.puts("✅ Database running: #{inspect(pid)}") end # 2. Check table exists case Sqler.tables(:compile_log_db) do tables when "auto_compile" in tables -> IO.puts("✅ Table exists") tables -> IO.puts("❌ Table missing. Found: #{inspect(tables)}") CompileLog.setup_database() end # 3. Test manual logging case CompileLog.log("lib/test.ex") do {:ok, id} -> IO.puts("✅ Logging works: #{id}") {:error, reason} -> IO.puts("❌ Logging failed: #{reason}") end # 4. Check recent logs case CompileLog.get_recent(5) do [] -> IO.puts("❌ No logs found") logs -> IO.puts("✅ Found #{length(logs)} logs") end ``` ### Git Diff Returns nil ```bash # Not a git repository cd /path/to/project git status # If not a git repo: git init # File not tracked git add lib/your_file.ex git status # Test git diff manually git diff -U0 lib/your_file.ex ``` ```elixir # Test from Elixir CompileLog.get_git_diff("lib/user.ex") |> case do nil -> IO.puts("❌ Git diff failed") "" -> IO.puts("✅ No changes (clean)") diff -> IO.puts("✅ Diff:\n#{diff}") end ``` ### Database Errors ```elixir # Check database file case File.stat("data/log.sqlite") do {:ok, stat} -> IO.puts("✅ Database file exists: #{stat.size} bytes") {:error, :enoent} -> IO.puts("❌ Database file missing") CompileLog.setup_database() end # Test database connection case Sqler.sql(:compile_log_db, "SELECT 1") do [[1]] -> IO.puts("✅ Database connection OK") error -> IO.puts("❌ Database error: #{inspect(error)}") end # Re-initialize if needed CompileLog.setup_database() ``` ### Performance Issues ```elixir # Check database size size_kb = div(File.stat!("data/log.sqlite").size, 1024) IO.puts("Database size: #{size_kb} KB") if size_kb > 10_000 do IO.puts("⚠️ Database is large (>10MB), consider cleanup") CompileLog.cleanup_old_logs(30) end # Check log count stats = CompileLog.stats() IO.puts("Total logs: #{stats.total_compilations}") if stats.total_compilations > 10_000 do IO.puts("⚠️ Many logs, cleanup recommended") end ``` --- **Documentation Generated:** 2026-01-31 **Module Version:** 1.0.0 **Elixir Version:** 1.19.5