From 7262b8bcd812425cd8bfe68b39301edaf4bc89df Mon Sep 17 00:00:00 2001 From: liorix Date: Thu, 17 Jul 2025 21:59:12 -0400 Subject: [PATCH 1/2] fix: add Elixir 1.19 type system compatibility Fixes type inference warnings introduced in Elixir 1.19's enhanced type system: - Fix "expected a module" warnings by normalizing @message_builder at compile time - Fix "unreachable clause" warnings by using proper with expressions and case statements - Fix "never used" warnings by consolidating handle_init_result function clauses - Add missing lvs_refresh handler for complete Phoenix Channel compatibility - Use function_exported?/3 for robust message builder arity detection - Remove unused alias in mix task Changes maintain full backward compatibility with existing LiveState applications while eliminating all type system violations in Elixir 1.19. Resolves compilation warnings without breaking existing functionality. --- lib/live_state/channel.ex | 47 ++++++++++++++----------- lib/mix/tasks/live_state.gen.element.ex | 1 - 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/lib/live_state/channel.ex b/lib/live_state/channel.ex index 815e439..9b4ce91 100644 --- a/lib/live_state/channel.ex +++ b/lib/live_state/channel.ex @@ -15,7 +15,6 @@ defmodule LiveState.Channel do """ import Phoenix.Socket - alias Phoenix.Socket alias LiveState.Event @doc """ @@ -87,34 +86,36 @@ defmodule LiveState.Channel do @dialyzer {:nowarn_function, update_state: 2} @behaviour unquote(__MODULE__) - @message_builder unquote( + @message_builder (case unquote( Keyword.get( opts, :message_builder, {LiveState.MessageBuilder, ignore_keys: [:__meta__]} ) - ) + ) do + {mod, opts} when is_atom(mod) -> {mod, opts} + mod when is_atom(mod) -> {mod, []} + end) @max_version unquote(Keyword.get(opts, :max_version, 1000)) def join(channel, payload, socket) do - case authorize(channel, payload, socket) do - {:ok, socket} -> - send(self(), {:after_join, channel, payload}) - {:ok, socket} - - {:error, reason} -> - {:error, reason} + with {:ok, socket} <- authorize(channel, payload, socket) do + send(self(), {:after_join, channel, payload}) + {:ok, socket} end end def handle_info({:after_join, channel, payload}, socket) do - case init(channel, payload, socket) do - {:ok, state, socket} -> - {:noreply, initialize_state(state, socket)} + init(channel, payload, socket) + |> handle_init_result(socket) + end - {:ok, state} -> + defp handle_init_result(result, socket) do + case result do + {:ok, state, new_socket} when is_map(state) -> + {:noreply, initialize_state(state, new_socket)} + {:ok, state} when is_map(state) -> {:noreply, initialize_state(state, socket)} - {:error, error} -> push_error(socket, error) {:noreply, socket} @@ -173,16 +174,20 @@ defmodule LiveState.Channel do end defp build_update_message(current_state, new_state, version) do - case @message_builder do - {mod, opts} -> mod.update_state_message(current_state, new_state, version, opts) - mod -> mod.update_state_message(current_state, new_state, version) + {mod, opts} = @message_builder + if function_exported?(mod, :update_state_message, 4) do + mod.update_state_message(current_state, new_state, version, opts) + else + mod.update_state_message(current_state, new_state, version) end end defp build_new_state_message(new_state, version) do - case @message_builder do - {mod, opts} -> mod.new_state_message(new_state, version, opts) - mod -> mod.new_state_message(new_state, version) + {mod, opts} = @message_builder + if function_exported?(mod, :new_state_message, 3) do + mod.new_state_message(new_state, version, opts) + else + mod.new_state_message(new_state, version) end end diff --git a/lib/mix/tasks/live_state.gen.element.ex b/lib/mix/tasks/live_state.gen.element.ex index 3beab65..98965cb 100644 --- a/lib/mix/tasks/live_state.gen.element.ex +++ b/lib/mix/tasks/live_state.gen.element.ex @@ -16,7 +16,6 @@ defmodule Mix.Tasks.LiveState.Gen.Element do * a channel test in `test/my_app_web/channels` """ use Mix.Task - alias Mix.Tasks.Phx.Gen @doc false def run(args) do From 0b7b42926646065327f4db381f772bb466c7aca1 Mon Sep 17 00:00:00 2001 From: Chris Nelson Date: Sat, 19 Jul 2025 12:38:05 -0400 Subject: [PATCH 2/2] use elixir 1.19 locally and in build --- .github/workflows/ci.yml | 4 ++-- .tool-versions | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a02c4e..ba2dd32 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,8 +13,8 @@ jobs: - name: Set up Elixir uses: erlef/setup-beam@v1 with: - otp-version: '25.1' # Define the OTP version [required] - elixir-version: '1.14.0' # Define the elixir version [required] + otp-version: '27.2.4' # Define the OTP version [required] + elixir-version: '1.19.0-rc.0-otp-27' # Define the elixir version [required] - name: Restore dependencies cache uses: actions/cache@v4 with: diff --git a/.tool-versions b/.tool-versions index 6d03243..0800bec 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -elixir 1.15.7 -erlang 26.2.1 \ No newline at end of file +elixir 1.19.0-rc.0-otp-27 +erlang 27.2.4 \ No newline at end of file