Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace Finch for Req as default swoosh client in installer #5882

Merged
merged 1 commit into from
Aug 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion installer/templates/phx_single/config/prod.exs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Config
config :<%= @web_app_name %>, <%= @endpoint_module %>, cache_static_manifest: "priv/static/cache_manifest.json"<% end %><%= if @mailer do %>

# Configures Swoosh API Client
config :swoosh, api_client: Swoosh.ApiClient.Finch, finch_name: <%= @app_module %>.Finch
config :swoosh, api_client: Swoosh.ApiClient.Req

# Disable Swoosh Local Memory Storage
config :swoosh, local: false<% end %>
Expand Down
2 changes: 1 addition & 1 deletion installer/templates/phx_single/config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ if config_env() == :prod do
# domain: System.get_env("MAILGUN_DOMAIN")
#
# For this example you need include a HTTP client required by Swoosh API client.
# Swoosh supports Hackney and Finch out of the box:
# Swoosh supports Hackney, Req and Finch out of the box:
#
# config :swoosh, :api_client, Swoosh.ApiClient.Hackney
#
Expand Down
4 changes: 1 addition & 3 deletions installer/templates/phx_single/lib/app_name/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ defmodule <%= @app_module %>.Application do
repos: Application.fetch_env!(<%= inspect(String.to_atom(@app_name)) %>, :ecto_repos),
skip: skip_migrations?()},<% end %>
{DNSCluster, query: Application.get_env(<%= inspect(String.to_atom(@app_name)) %>, :dns_cluster_query) || :ignore},
{Phoenix.PubSub, name: <%= @app_module %>.PubSub},<%= if @mailer do %>
# Start the Finch HTTP client for sending emails
{Finch, name: <%= @app_module %>.Finch},<% end %>
{Phoenix.PubSub, name: <%= @app_module %>.PubSub},
# Start a worker by calling: <%= @app_module %>.Worker.start_link(arg)
# {<%= @app_module %>.Worker, arg},
# Start to serve requests, typically the last entry
Expand Down
4 changes: 2 additions & 2 deletions installer/templates/phx_single/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ defmodule <%= @app_module %>.MixProject do
app: false,
compile: false,
depth: 1},<% end %><%= if @mailer do %>
{:swoosh, "~> 1.5"},
{:finch, "~> 0.13"},<% end %>
{:swoosh, "~> 1.16"},
{:req, "~> 0.5.4"},<% end %>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably relax it a bit?

Suggested change
{:req, "~> 0.5.4"},<% end %>
{:req, "~> 0.5"},<% end %>

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went for 0.5.x because I thought 0.6 may introduce breaking changes. Happy to go with either though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you prefer! It is only for new apps and there will be a lock file anyway. And we can always update it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just so you know, I'm considering some changes before Req v1.0 that are maybe worth mentioning here:

  1. Since day 1, Req retries on errors by default. I've had some feedback over the years this is a bit surprising. There's a recently opened issue too: Retrying by default is a bit surprising wojtekmach/req#383. If you'd like to chime in, that's appreciated. I added some extra context in Swoosh too: https://github.com/swoosh/swoosh/pull/830/files#r1694016381.

  2. Since day 1, Req brings in jason dependency. I'd like to drop it before v1.0 in favour of built-in :json from OTP 27. I'd be OK even conditionally vendoring it in for pre-27. This is tracked here: json: Support optional use of :json in OTP 27+ wojtekmach/req#386.

Btw, Finch is dropping :castore dependency and once Finch is out with this change, I'm keen on requiring that Finch version as minimum in Req. I'd probably cut Req v0.6.0 for this change. This is effectively requiring OTP 25 (and running on system that have built-in CA) for everything to work out of the box.

{:req, "~> 0.5.4"} is definitely good for now. I'd be happy to send a patch bumping that to ~> 0.6/1.0 in the future.

Dunno how relevant are these for Phoenix but I thought Id mentioned it anyway!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both Swoosh and Phoenix depends on Jason, so making it optional on Req won't affect us. I also don't think OTP 25 requirement is an issue, this is for new projects and that's the minimum supported OTP version nowadays anyway.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding req retries by default and swoosh, this is all moot, sorry about the noise. In req the default is retry: :safe_transient, safe meaning only for GET/HEAD requests. Sending emails would be typically done using POST. So we are good.

{:telemetry_metrics, "~> 1.0"},
{:telemetry_poller, "~> 1.0"},<%= if @gettext do %>
{:gettext, "~> 0.20"},<% end %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ defmodule <%= @app_module %>.Application do
repos: Application.fetch_env!(<%= inspect(String.to_atom(@app_name)) %>, :ecto_repos),
skip: skip_migrations?()},<% end %>
{DNSCluster, query: Application.get_env(<%= inspect(String.to_atom(@app_name)) %>, :dns_cluster_query) || :ignore},
{Phoenix.PubSub, name: <%= @app_module %>.PubSub}<%= if @mailer do %>,
# Start the Finch HTTP client for sending emails
{Finch, name: <%= @app_module %>.Finch}<% end %>
{Phoenix.PubSub, name: <%= @app_module %>.PubSub}
# Start a worker by calling: <%= @app_module %>.Worker.start_link(arg)
# {<%= @app_module %>.Worker, arg}
]
Expand Down
4 changes: 2 additions & 2 deletions installer/templates/phx_umbrella/apps/app_name/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ defmodule <%= @app_module %>.MixProject do
{:ecto_sql, "~> 3.10"},
{:<%= @adapter_app %>, ">= 0.0.0"},
{:jason, "~> 1.2"}<% end %><%= if @mailer do %>,
{:swoosh, "~> 1.5"},
{:finch, "~> 0.13"}<% end %>
{:swoosh, "~> 1.16"},
{:req, "~> 0.5.4"}<% end %>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{:req, "~> 0.5.4"}<% end %>
{:req, "~> 0.5"}<% end %>

]
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ config :<%= @web_app_name %>, <%= @endpoint_module %>,
# domain: System.get_env("MAILGUN_DOMAIN")
#
# For this example you need include a HTTP client required by Swoosh API client.
# Swoosh supports Hackney and Finch out of the box:
# Swoosh supports Hackney, Req and Finch out of the box:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should just remove this section from the docs? We already config a client for prod anyway?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, keep the part about configuring the adopter, drop the part about the HTTP Client.

#
# config :swoosh, :api_client, Swoosh.ApiClient.Hackney
#
Expand Down
2 changes: 1 addition & 1 deletion installer/templates/phx_umbrella/config/prod.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Config

<%= if @mailer do %>
# Configures Swoosh API Client
config :swoosh, :api_client, <%= @app_module %>.Finch
config :swoosh, :api_client, Swoosh.ApiClient.Req

# Disable Swoosh Local Memory Storage
config :swoosh, local: false<% end %>
Expand Down
18 changes: 5 additions & 13 deletions installer/test/phx_new_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -257,12 +257,8 @@ defmodule Mix.Tasks.Phx.NewTest do

# Mailer
assert_file("phx_blog/mix.exs", fn file ->
assert file =~ "{:swoosh, \"~> 1.5\"}"
assert file =~ "{:finch, \"~> 0.13\"}"
end)

assert_file("phx_blog/lib/phx_blog/application.ex", fn file ->
assert file =~ "{Finch, name: PhxBlog.Finch}"
assert file =~ "{:swoosh, \"~> 1.16\"}"
assert file =~ "{:req, \"~> 0.5.4\"}"
end)

assert_file("phx_blog/lib/phx_blog/mailer.ex", fn file ->
Expand All @@ -285,7 +281,7 @@ defmodule Mix.Tasks.Phx.NewTest do

assert_file("phx_blog/config/prod.exs", fn file ->
assert file =~
"config :swoosh, api_client: Swoosh.ApiClient.Finch, finch_name: PhxBlog.Finch"
"config :swoosh, api_client: Swoosh.ApiClient.Req"
end)

# Install dependencies?
Expand Down Expand Up @@ -417,12 +413,8 @@ defmodule Mix.Tasks.Phx.NewTest do

# No mailer or emails
assert_file("phx_blog/mix.exs", fn file ->
refute file =~ "{:swoosh, \"~> 1.5\"}"
refute file =~ "{:finch, \"~> 0.13\"}"
end)

assert_file("phx_blog/lib/phx_blog/application.ex", fn file ->
refute file =~ "{Finch, name: PhxBlog.Finch"
refute file =~ "{:swoosh"
refute file =~ "{:req"
end)

refute File.exists?("phx_blog/lib/phx_blog/mailer.ex")
Expand Down
18 changes: 5 additions & 13 deletions installer/test/phx_new_umbrella_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -277,12 +277,8 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do

# Mailer
assert_file(app_path(@app, "mix.exs"), fn file ->
assert file =~ "{:swoosh, \"~> 1.5\"}"
assert file =~ "{:finch, \"~> 0.13\"}"
end)

assert_file(app_path(@app, "lib/#{@app}/application.ex"), fn file ->
assert file =~ "{Finch, name: PhxUmb.Finch}"
assert file =~ "{:swoosh, \"~> 1.16\"}"
assert file =~ "{:req, \"~> 0.5.4\"}"
end)

assert_file(app_path(@app, "lib/#{@app}/mailer.ex"), fn file ->
Expand All @@ -304,7 +300,7 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do
end)

assert_file(root_path(@app, "config/prod.exs"), fn file ->
assert file =~ "config :swoosh, :api_client, PhxUmb.Finch"
assert file =~ "config :swoosh, :api_client, Swoosh.ApiClient.Req"
end)

# Install dependencies?
Expand Down Expand Up @@ -413,12 +409,8 @@ defmodule Mix.Tasks.Phx.New.UmbrellaTest do

# Without mailer
assert_file(web_path(@app, "mix.exs"), fn file ->
refute file =~ "{:swoosh, \"~> 1.5\"}"
refute file =~ "{:finch, \"~> 0.13\"}"
end)

assert_file(app_path(@app, "lib/#{@app}/application.ex"), fn file ->
refute file =~ "{Finch, name: PhxUmb.Finch}"
refute file =~ "{:swoosh"
refute file =~ "{:req"
end)

refute File.exists?(app_path(@app, "lib/#{@app}/mailer.ex"))
Expand Down
4 changes: 2 additions & 2 deletions integration_test/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ defmodule Phoenix.Integration.MixProject do
{:telemetry_poller, "~> 1.0"},
{:gettext, "~> 0.20"},
{:jason, "~> 1.2"},
{:swoosh, "~> 1.3"},
{:swoosh, "~> 1.16"},
{:bandit, "~> 1.0"},
{:bcrypt_elixir, "~> 3.0"},
{:argon2_elixir, "~> 3.0"},
Expand All @@ -64,7 +64,7 @@ defmodule Phoenix.Integration.MixProject do
app: false,
compile: false,
depth: 1},
{:finch, "~> 0.13"}
{:req, "~> 0.5.4"}
]
end
end
13 changes: 7 additions & 6 deletions integration_test/mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"argon2_elixir": {:hex, :argon2_elixir, "3.2.1", "f47740bf9f2a39ffef79ba48eb25dea2ee37bcc7eadf91d49615591d1a6fce1a", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "a813b78217394530b5fcf4c8070feee43df03ffef938d044019169c766315690"},
"bandit": {:hex, :bandit, "1.5.4", "8e56e7cfc06f3c57995be0d9bf4e45b972d8732f5c7e96ef8ec0735f52079527", [:mix], [{:hpax, "~> 0.2.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "04c2b38874769af67fe7f10034f606ad6dda1d8f80c4d7a0c616b347584d5aff"},
"bcrypt_elixir": {:hex, :bcrypt_elixir, "3.1.0", "0b110a9a6c619b19a7f73fa3004aa11d6e719a67e672d1633dc36b6b2290a0f7", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "2ad2acb5a8bc049e8d5aa267802631912bb80d5f4110a178ae7999e69dca1bf7"},
"castore": {:hex, :castore, "1.0.7", "b651241514e5f6956028147fe6637f7ac13802537e895a724f90bf3e36ddd1dd", [:mix], [], "hexpm", "da7785a4b0d2a021cd1292a60875a784b6caef71e76bf4917bdee1f390455cf5"},
"castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"},
"cc_precompiler": {:hex, :cc_precompiler, "0.1.10", "47c9c08d8869cf09b41da36538f62bc1abd3e19e41701c2cea2675b53c704258", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f6e046254e53cd6b41c6bacd70ae728011aa82b2742a80d6e2214855c6e06b22"},
"comeonin": {:hex, :comeonin, "5.4.0", "246a56ca3f41d404380fc6465650ddaa532c7f98be4bda1b4656b3a37cc13abe", [:mix], [], "hexpm", "796393a9e50d01999d56b7b8420ab0481a7538d0caf80919da493b4a6e51faf1"},
"db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"},
Expand All @@ -19,11 +19,11 @@
"finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"},
"floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"},
"gettext": {:hex, :gettext, "0.24.0", "6f4d90ac5f3111673cbefc4ebee96fe5f37a114861ab8c7b7d5b30a1108ce6d8", [:mix], [{:expo, "~> 0.5.1", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "bdf75cdfcbe9e4622dd18e034b227d77dd17f0f133853a1c73b97b3d6c770e8b"},
"heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "88ab3a0d790e6a47404cba02800a6b25d2afae50", [tag: "v2.1.1", sparse: "optimized"]},
"heroicons": {:git, "https://github.com/tailwindlabs/heroicons.git", "88ab3a0d790e6a47404cba02800a6b25d2afae50", [tag: "v2.1.1", sparse: "optimized", depth: 1]},
"hpax": {:hex, :hpax, "0.2.0", "5a58219adcb75977b2edce5eb22051de9362f08236220c9e859a47111c194ff5", [:mix], [], "hexpm", "bea06558cdae85bed075e6c036993d43cd54d447f76d8190a8db0dc5893fa2f1"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
"mint": {:hex, :mint, "1.6.1", "065e8a5bc9bbd46a41099dfea3e0656436c5cbcb6e741c80bd2bad5cd872446f", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "4fc518dcc191d02f433393a72a7ba3f6f94b101d094cb6bf532ea54c89423780"},
"jason": {:hex, :jason, "1.4.3", "d3f984eeb96fe53b85d20e0b049f03e57d075b5acda3ac8d465c969a2536c17b", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3"},
"mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"},
"mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"},
"myxql": {:hex, :myxql, "0.7.0", "3382f139b0b0da977a8fc33c8cded125e20df2e400f8d7b7e674fa62a7e077dd", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:geo, "~> 3.4", [hex: :geo, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "40e4b7ad4973c8b895e86a3de04ff7a79c2cf72b9f2bddef7717afb4ab36d8c0"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
Expand All @@ -35,9 +35,10 @@
"phoenix_live_view": {:hex, :phoenix_live_view, "1.0.0-rc.1", "adfb0ab3edbad3bac63661ee0367c803d96178b8e8bbb8ff2b6c7cebdb820491", [:mix], [{:floki, "~> 0.36", [hex: :floki, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ffc940d7af43c3d6c26faa7693872f95178f4ca886434224d29d4b54d3a38ae3"},
"phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"},
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
"plug": {:hex, :plug, "1.16.0", "1d07d50cb9bb05097fdf187b31cf087c7297aafc3fed8299aac79c128a707e47", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "cbf53aa1f5c4d758a7559c0bd6d59e286c2be0c6a1fac8cc3eee2f638243b93e"},
"plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"},
"plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"},
"postgrex": {:hex, :postgrex, "0.18.0", "f34664101eaca11ff24481ed4c378492fed2ff416cd9b06c399e90f321867d7e", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "a042989ba1bc1cca7383ebb9e461398e3f89f868c92ce6671feb7ef132a252d1"},
"req": {:hex, :req, "0.5.4", "e375e4812adf83ffcf787871d7a124d873e983e3b77466e6608b973582f7f837", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "a17998ffe2ef54f79bfdd782ef9f4cbf987d93851e89444cbc466a6a25eee494"},
"swoosh": {:hex, :swoosh, "1.16.9", "20c6a32ea49136a4c19f538e27739bb5070558c0fa76b8a95f4d5d5ca7d319a1", [:mix], [{:bandit, ">= 1.0.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mua, "~> 0.2.0", [hex: :mua, repo: "hexpm", optional: true]}, {:multipart, "~> 0.4", [hex: :multipart, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: true]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:req, "~> 0.5 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "878b1a7a6c10ebbf725a3349363f48f79c5e3d792eb621643b0d276a38acc0a6"},
"tailwind": {:hex, :tailwind, "0.2.3", "277f08145d407de49650d0a4685dc062174bdd1ae7731c5f1da86163a24dfcdb", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "8e45e7a34a676a7747d04f7913a96c770c85e6be810a1d7f91e713d3a3655b5d"},
"tds": {:hex, :tds, "2.3.5", "fedfb96d53206f01eac62ead859e47e1541a62e1553e9eb7a8801c7dca59eae8", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.9 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "52e350f5dd5584bbcff9859e331be144d290b41bd4c749b936014a17660662f2"},
Expand Down