ref:21dd13cd9a0130ff50d0fb3568f5018687f6cf1c

chore(deps): Elixir 1.20 / OTP 29 readiness — fix warnings, re-pin deps, ex_aws→Req (#41)

Makes the library clean and current under **Elixir 1.20 / Erlang 29**, and unblocks Anvil's toolchain upgrade (Anvil consumes this lib). ## Changes - **Fix all 22 bitstring `size(^var)` pin warnings** — Elixir 1.20 requires the pin operator on outer variables used inside `binary-size()` within a match. Across `pkt_line`, `pack/{reader,index,delta}`, `lfs/store/s3`, `protocol/receive_pack`. Literals (`binary-size(4)`, `binary-size(20)`) and `@trailer_size` left untouched. - **`upload_pack.sanitize_error/1`**: drop the unreachable atom/tuple/catch-all clauses (the 1.20 type checker proves all callers pass a binary; the file already had `@dialyzer nowarn` acknowledging they were dead) and the now-moot suppression. A non-binary reason would now `FunctionClauseError` loudly here. - **Re-pin all deps to latest**: ex_aws 2.7, telemetry 1.4, bandit 1.11, plug 1.19, ex_doc 0.40, **credo 1.7.18** (1.7.16 *crashed* under Elixir 1.20), stream_data 1.3. Elixir requirement → `~> 1.20`. - **Switch ex_aws's HTTP client from hackney to its `Req` adapter; drop the hackney dependency.** hackney 4.x broke ex_aws's bundled hackney adapter (`CaseClauseError` on the response shape) — the `:s3` conformance tests caught it against MinIO. `Req` is modern/maintained and verified. `http_client` is set in `config/config.exs` for this lib's own dev/test only (a dependency's config isn't loaded by consumers — apps set their own `:ex_aws, :http_client`). ## Verification - `mix compile --warnings-as-errors` — clean - `mix format --check-formatted` — clean - `mix dialyzer` — **0 errors** - `mix test --include s3` — **1024 passed** (incl. the `:s3` ex_aws→Req→S3 conformance tests against a live MinIO) Note: credo isn't in this repo's CI gate (compile-WAE + format + dialyzer + test); pre-existing `credo --strict` style suggestions are out of scope here. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
SHA: 21dd13cd9a0130ff50d0fb3568f5018687f6cf1c
Author: Anvil <noreply@anvil.fangorn.io>
Date: 2026-06-04 01:25
Parents: 6cddd4f
11 files changed +86 -61
Type
.anvil.yml +13 −9
@@ -1,10 +1,21 @@
# No final Elixir 1.20.0 Docker image exists yet (hexpm/elixir:1.20 resolves
# to 1.20.0-rc.6, which mix.exs's `~> 1.20` rejects), so base on the Erlang
# 29.0.1 image and install the final 1.20.0 (otp-29) precompiled build in
# `prepare` (runs once per worker). Switch to a
# hexpm/elixir:1.20.0-erlang-29.0.1-debian-trixie-* image once one exists.
image: hexpm/erlang:29.0.1-debian-trixie-20260518
image: hexpm/elixir:1.18.4-erlang-28.0.2-debian-bookworm-20250811
prepare:
- apt-get update && apt-get install -y --no-install-recommends git build-essential curl unzip ca-certificates
- curl -fSL -o /tmp/elixir.zip "https://builds.hex.pm/builds/elixir/v1.20.0-otp-29.zip"
- echo "eb04bbe0748cfc1414081a005c711a102d0b3f551ff96cc8cff81b0e7d468f19 /tmp/elixir.zip" | sha256sum -c -
- unzip -q -o /tmp/elixir.zip -d /usr/local && rm /tmp/elixir.zip
- mix local.hex --force && mix local.rebar --force
steps:
- name: deps
run: |
set -e
apt-get update && apt-get install -y --no-install-recommends git build-essential
git config --global --add safe.directory /workspace
export MIX_HOME=/workspace/.mix
mix local.hex --force && mix local.rebar --force
@@ -31,7 +42,6 @@
- name: test
run: |
set -e
apt-get update && apt-get install -y --no-install-recommends git
git config --global --add safe.directory /workspace
git config --global init.defaultBranch main
git config --global user.email "ci@anvil.test"
@@ -70,6 +80,5 @@
- name: release
run: |
set -e
apt-get update && apt-get install -y --no-install-recommends git jq curl
git config --global --add safe.directory /workspace
export MIX_HOME=/workspace/.mix
@@ -101,11 +110,6 @@
# an admin must grant `releases: write` via:
# PUT /api/v1/fangorn/ex_git_objectstore/ci/permissions
# See fangorn/anvil#46 for the full design.
#
# `anvil auth status` fails loud if the runner didn't inject the
# token, if it's malformed, or if the server rejects it — which
# is exactly what we want before `anvil release create` tries to
# use it downstream.
/usr/local/bin/anvil auth status
# Compute CalVer version
config/config.exs +20 −0
@@ -1,0 +1,20 @@
# Copyright 2026 Cole Christensen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import Config
# Use ex_aws's Req HTTP client. This config applies to ex_git_objectstore's
# own dev/test runs only — a dependency's config is not loaded by consumers,
# so apps embedding this library set their own `:ex_aws, :http_client`.
config :ex_aws, http_client: ExAws.Request.Req
lib/ex_git_objectstore/lfs/store/s3.ex +1 −1
@@ -108,7 +108,7 @@
end
defp emit_full_parts(%{buffer: buf, part_size: ps} = state) when byte_size(buf) >= ps do
<<part::binary-size(ps), rest::binary>> = buf
<<part::binary-size(^ps), rest::binary>> = buf
{:ok, etag} = upload_part(state.config, state.key, state.upload_id, state.part_number, part)
%{
lib/ex_git_objectstore/pack/delta.ex +1 −1
@@ -122,7 +122,7 @@
size = cmd
if byte_size(rest) >= size do
<<literal::binary-size(size), remaining::binary>> = rest
<<literal::binary-size(^size), remaining::binary>> = rest
apply_instructions(remaining, base, [literal | acc])
else
{:error, :truncated_insert}
lib/ex_git_objectstore/pack/index.ex +5 −5
@@ -77,7 +77,7 @@
# Verify the trailing 20-byte index checksum
defp verify_index_checksum(idx_data) when byte_size(idx_data) >= 28 do
body_len = byte_size(idx_data) - 20
<<body::binary-size(body_len), checksum::binary-size(20)>> = idx_data
<<body::binary-size(^body_len), checksum::binary-size(20)>> = idx_data
expected = :crypto.hash(:sha, body)
if checksum == expected do
@@ -141,7 +141,7 @@
sha_bytes = count * 20
if byte_size(data) >= sha_bytes do
<<sha_bin::binary-size(^sha_bytes), rest::binary>> = data
<<sha_bin::binary-size(sha_bytes), rest::binary>> = data
shas =
for <<sha::binary-size(20) <- sha_bin>> do
@@ -158,7 +158,7 @@
crc_bytes = count * 4
if byte_size(data) >= crc_bytes do
<<crc_bin::binary-size(crc_bytes), rest::binary>> = data
<<crc_bin::binary-size(^crc_bytes), rest::binary>> = data
crcs =
for <<crc::unsigned-big-32 <- crc_bin>> do
@@ -175,7 +175,7 @@
offset_bytes = count * 4
if byte_size(data) >= offset_bytes do
<<offset_bin::binary-size(^offset_bytes), rest::binary>> = data
<<offset_bin::binary-size(offset_bytes), rest::binary>> = data
offsets =
for <<offset::unsigned-big-32 <- offset_bin>> do
@@ -204,7 +204,7 @@
large_bytes = large_count * 8
if byte_size(rest) >= large_bytes do
<<large_bin::binary-size(large_bytes), _rest::binary>> = rest
<<large_bin::binary-size(^large_bytes), _rest::binary>> = rest
large_offsets =
for <<offset::unsigned-big-64 <- large_bin>> do
lib/ex_git_objectstore/pack/reader.ex +11 −11
@@ -217,7 +217,7 @@
defp stream_entries(_pack_data, 0, _abs_offset, state), do: {:ok, state}
defp stream_entries(pack_data, remaining, abs_offset, state) do
<<_skip::binary-size(abs_offset), rest::binary>> = pack_data
<<_skip::binary-size(^abs_offset), rest::binary>> = pack_data
case parse_entry_at(rest) do
{:ok, entry, consumed} ->
@@ -410,7 +410,7 @@
@spec verify_pack_checksum(binary()) :: :ok | {:error, term()}
def verify_pack_checksum(pack_data) when byte_size(pack_data) >= 32 do
body_len = byte_size(pack_data) - 20
<<body::binary-size(body_len), checksum::binary-size(20)>> = pack_data
<<body::binary-size(^body_len), checksum::binary-size(20)>> = pack_data
expected = :crypto.hash(:sha, body)
if checksum == expected do
@@ -523,7 +523,7 @@
end
defp resolve_object(pack_data, offset, cache, depth, external_resolver) do
<<_skip::binary-size(^offset), data::binary>> = pack_data
<<_skip::binary-size(offset), data::binary>> = pack_data
with {:ok, type_num, _size, header_len, _rest} <- parse_object_header(data) do
resolve_object_by_type(
@@ -542,7 +542,7 @@
when t in @base_types do
type = type_num_to_atom(t)
data_start = offset + header_len
<<_::binary-size(data_start), compressed::binary>> = pack_data
<<_::binary-size(^data_start), compressed::binary>> = pack_data
case decompress_data(compressed) do
{:ok, decompressed, _} -> {:ok, {type, decompressed}}
@@ -552,14 +552,14 @@
defp resolve_object_by_type(@obj_ofs_delta, pack_data, offset, header_len, cache, depth, ext) do
data_start = offset + header_len
<<_::binary-size(^data_start), rest::binary>> = pack_data
<<_::binary-size(data_start), rest::binary>> = pack_data
resolve_ofs_delta(rest, pack_data, offset, data_start, cache, depth, ext)
end
defp resolve_object_by_type(@obj_ref_delta, pack_data, offset, header_len, cache, depth, ext) do
data_start = offset + header_len
<<_::binary-size(data_start), base_sha_bin::binary-size(20), compressed::binary>> = pack_data
<<_::binary-size(^data_start), base_sha_bin::binary-size(20), compressed::binary>> = pack_data
base_sha = Base.encode16(base_sha_bin, case: :lower)
resolve_ref_delta(compressed, base_sha, pack_data, cache, depth, ext)
@@ -577,7 +577,7 @@
{:error, :invalid_ofs_delta_offset}
else
delta_start = data_start + ofs_len
<<_::binary-size(^delta_start), compressed::binary>> = pack_data
<<_::binary-size(delta_start), compressed::binary>> = pack_data
apply_delta_from_pack(compressed, pack_data, base_offset, cache, depth, ext)
end
end
@@ -771,7 +771,7 @@
defp probe_compressed_length(_compressed, _expected_size, low, _high), do: low
defp try_decompress_prefix(compressed, len) do
<<prefix::binary-size(len), _rest::binary>> = compressed
<<prefix::binary-size(^len), _rest::binary>> = compressed
z = :zlib.open()
try do
@@ -857,7 +857,7 @@
{:ok, content, compressed_len} ->
sha = compute_object_sha(type_str, content)
consumed = header_len + compressed_len
<<_::binary-size(^consumed), next_data::binary>> = data
<<_::binary-size(consumed), next_data::binary>> = data
scan_entries_for_index(
next_data,
@@ -875,7 +875,7 @@
with {:ok, _neg_offset, ofs_len, rest_after_ofs} <- parse_ofs_delta_offset(rest),
{:ok, _delta_data, compressed_len} <- decompress_data(rest_after_ofs) do
consumed = header_len + ofs_len + compressed_len
<<_::binary-size(^consumed), next_data::binary>> = data
<<_::binary-size(consumed), next_data::binary>> = data
scan_entries_for_index(next_data, remaining - 1, offset + consumed, index)
end
end
@@ -891,7 +891,7 @@
case decompress_data(rest_after_sha) do
{:ok, _delta_data, compressed_len} ->
consumed = header_len + 20 + compressed_len
<<_::binary-size(consumed), next_data::binary>> = data
<<_::binary-size(^consumed), next_data::binary>> = data
scan_entries_for_index(next_data, remaining - 1, offset + consumed, index)
{:error, _} = err ->
lib/ex_git_objectstore/protocol/pkt_line.ex +3 −3
@@ -143,7 +143,7 @@
case Integer.parse(hex_len, 16) do
{len, ""} when len >= 4 and len <= @max_pkt_len ->
if byte_size(data) >= len do
<<_hex::binary-size(4), payload::binary-size(len - 4), rest::binary>> = data
<<_hex::binary-size(4), payload::binary-size(^len - 4), rest::binary>> = data
# Text frames strip a trailing LF (spec convention, pairs with
# encode/1); binary/sideband frames (:verbatim) keep every byte.
payload = if mode == :strip_lf, do: strip_one_trailing_lf(payload), else: payload
@@ -230,7 +230,7 @@
size = byte_size(bin) - 1
case bin do
<<prefix::binary-size(size), "\n">> -> prefix
<<prefix::binary-size(^size), "\n">> -> prefix
_ -> bin
end
end
@@ -240,7 +240,7 @@
defp chunk_binary(data, size) when byte_size(data) <= size, do: [data]
defp chunk_binary(data, size) do
<<chunk::binary-size(^size), rest::binary>> = data
<<chunk::binary-size(size), rest::binary>> = data
[chunk | chunk_binary(rest, size)]
end
end
lib/ex_git_objectstore/protocol/receive_pack.ex +1 −1
@@ -278,7 +278,7 @@
%{acc | lookahead: combined}
graduate_size ->
<<graduate::binary-size(graduate_size), new_lookahead::binary-size(@trailer_size)>> =
<<graduate::binary-size(^graduate_size), new_lookahead::binary-size(@trailer_size)>> =
combined
%{
lib/ex_git_objectstore/protocol/upload_pack.ex +4 −5
@@ -347,12 +347,11 @@
end
end
# Defensive: handle various error shapes even though current callers only pass binary
# All callers pass a binary error message. The previous atom/tuple/catch-all
# fallbacks were unreachable (Elixir 1.20's type checker proves it, and the
# @dialyzer nowarn this used to carry is no longer needed). If a non-binary
# reason is ever introduced, the resulting FunctionClauseError points here.
@dialyzer {:nowarn_function, sanitize_error: 1}
defp sanitize_error(reason) when is_binary(reason), do: reason
defp sanitize_error(reason) when is_atom(reason), do: Atom.to_string(reason)
defp sanitize_error({tag, _details}) when is_atom(tag), do: Atom.to_string(tag)
defp sanitize_error(_), do: "internal_error"
defp collect_objects(repo, wants, haves) do
# Walk from each want SHA and collect all reachable objects
mix.exs +13 −8
@@ -22,7 +22,7 @@
[
app: :ex_git_objectstore,
version: @version,
elixir: "~> 1.18",
elixir: "~> 1.20",
start_permanent: Mix.env() == :prod,
deps: deps(),
elixirc_paths: elixirc_paths(Mix.env()),
@@ -91,18 +91,23 @@
defp deps do
[
# Capped below 2.7: ex_aws 2.7 requires hackney ~> 4.0, whose `h2`
# modules collide with `chatterbox` (opentelemetry_exporter) in
# consumers like Anvil. ex_aws 2.6 keeps hackney optional at 1.x.
{:ex_aws, "~> 2.6.0"},
{:ex_aws, "~> 2.5"},
{:ex_aws_s3, "~> 2.5"},
{:sweet_xml, "~> 0.7"},
# ex_aws's default HTTP client is Req; using it lets this library drop
# the hackney dependency entirely.
{:hackney, "~> 1.18"},
{:req, "~> 0.5"},
{:jason, "~> 1.4"},
{:telemetry, "~> 1.0"},
{:telemetry, "~> 1.4"},
{:ex_doc, "~> 0.40", only: :dev, runtime: false},
{:ex_doc, "~> 0.34", only: :dev, runtime: false},
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
{:plug, "~> 1.16", only: :test},
{:bandit, "~> 1.5", only: :test},
{:stream_data, "~> 1.1", only: :test}
{:plug, "~> 1.19", only: :test},
{:bandit, "~> 1.11", only: :test},
{:stream_data, "~> 1.3", only: :test}
]
end
end
mix.lock +14 −17
@@ -1,34 +1,31 @@
%{
"bandit": {:hex, :bandit, "1.10.4", "02b9734c67c5916a008e7eb7e2ba68aaea6f8177094a5f8d95f1fb99069aac17", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [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", "a5faf501042ac1f31d736d9d4a813b3db4ef812e634583b6a457b0928798a51d"},
"bandit": {:hex, :bandit, "1.11.1", "1eb33123cc3c17ae0c3447874eb83399ee530f960c39711ed240342fbd4865fa", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [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", "d4401016df9abbc6dcd325c0b78b2b193e7c7c96bb68f31e576112be025d84a5"},
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"certifi": {:hex, :certifi, "2.15.0", "0e6e882fcdaaa0a5a9f2b3db55b1394dba07e8d6d9bcad08318fb604c6839712", [:rebar3], [], "hexpm", "b147ed22ce71d72eafdad94f055165c1c182f61a2ff49df28bcc71d1d5b94a60"},
"credo": {:hex, :credo, "1.7.16", "a9f1389d13d19c631cb123c77a813dbf16449a2aebf602f590defa08953309d4", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "d0562af33756b21f248f066a9119e3890722031b6d199f22e3cf95550e4f1579"},
"credo": {:hex, :credo, "1.7.18", "5c5596bf7aedf9c8c227f13272ac499fe8eae6237bd326f2f07dfc173786f042", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "a189d164685fd945809e862fe76a7420c4398fa288d76257662aecb909d6b3e5"},
"dialyxir": {:hex, :dialyxir, "1.4.7", "dda948fcee52962e4b6c5b4b16b2d8fa7d50d8645bbae8b8685c3f9ecb7f5f4d", [:mix], [{:erlex, ">= 0.2.8", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "b34527202e6eb8cee198efec110996c25c5898f43a4094df157f8d28f27d9efe"},
"earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"},
"erlex": {:hex, :erlex, "0.2.8", "cd8116f20f3c0afe376d1e8d1f0ae2452337729f68be016ea544a72f767d9c12", [:mix], [], "hexpm", "9d66ff9fedf69e49dc3fd12831e12a8a37b76f8651dd21cd45fcf5561a8a7590"},
"erlex": {:hex, :erlex, "0.2.9", "7debbbaa9f4f368b8cd648983e0f1d7963028508e9c59e9d4ed504e94ef52a55", [:mix], [], "hexpm", "8cfffc0ec7159e6d73de2ab28a588064de80f88b2798d5cbe4482cbbc200178b"},
"ex_aws": {:hex, :ex_aws, "2.6.1", "194582c7b09455de8a5ab18a0182e6dd937d53df82be2e63c619d01bddaccdfa", [:mix], [{:configparser_ex, "~> 5.0", [hex: :configparser_ex, repo: "hexpm", optional: true]}, {:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: true]}, {:jsx, "~> 2.8 or ~> 3.0", [hex: :jsx, repo: "hexpm", optional: true]}, {:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:req, "~> 0.5.10 or ~> 0.6 or ~> 1.0", [hex: :req, repo: "hexpm", optional: true]}, {:sweet_xml, "~> 0.7", [hex: :sweet_xml, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "67842a08c90a1d9a09dbe4ac05754175c7ca253abe4912987c759395d4bd9d26"},
"ex_aws_s3": {:hex, :ex_aws_s3, "2.5.9", "862b7792f2e60d7010e2920d79964e3fab289bc0fd951b0ba8457a3f7f9d1199", [:mix], [{:ex_aws, "~> 2.0", [hex: :ex_aws, repo: "hexpm", optional: false]}, {:sweet_xml, ">= 0.0.0", [hex: :sweet_xml, repo: "hexpm", optional: true]}], "hexpm", "a480d2bb2da64610014021629800e1e9457ca5e4a62f6775bffd963360c2bf90"},
"ex_doc": {:hex, :ex_doc, "0.40.1", "67542e4b6dde74811cfd580e2c0149b78010fd13001fda7cfeb2b2c2ffb1344d", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "bcef0e2d360d93ac19f01a85d58f91752d930c0a30e2681145feea6bd3516e00"},
"ex_doc": {:hex, :ex_doc, "0.40.3", "4a972ffe64bc07dc605af487e98fc19b72a4185f55ca031b94c0552d6071c1d9", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2756e357742fecd9749b489b85d67c9ce99c465f2e75728d9e6dc8d704b973de"},
"file_system": {:hex, :file_system, "1.1.1", "31864f4685b0148f25bd3fbef2b1228457c0c89024ad67f7a81a3ffbc0bbad3a", [:mix], [], "hexpm", "7a15ff97dfe526aeefb090a7a9d3d03aa907e100e262a0f8f7746b78f8f87a5d"},
"hackney": {:hex, :hackney, "1.25.0", "390e9b83f31e5b325b9f43b76e1a785cbdb69b5b6cd4e079aa67835ded046867", [:rebar3], [{:certifi, "~> 2.15.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.4", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "7209bfd75fd1f42467211ff8f59ea74d6f2a9e81cbcee95a56711ee79fd6b1d4"},
"finch": {:hex, :finch, "0.22.0", "5c48fa6f9706a78eb9036cacb67b8b996b4e66d111c543f4c29bb0f879a6806b", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.8", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b94e83c47780fc6813f746a1f1a34ee65cda42da4c5ea26a68f0acc4498e23dc"},
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"},
"makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"},
"makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"},
"makeup_erlang": {:hex, :makeup_erlang, "1.0.3", "4252d5d4098da7415c390e847c814bad3764c94a814a0b4245176215615e1035", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "953297c02582a33411ac6208f2c6e55f0e870df7f80da724ed613f10e6706afd"},
"makeup_erlang": {:hex, :makeup_erlang, "1.1.0", "835f7e60792e08824cda445639555d7bf1bbbddb1b60b306e33cb6f6db24dc74", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "1cd6780fb1dd1a03979abaed0fe82712b0625118fd5257d3ebbf73f960c73c3c"},
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
"mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"},
"mimerl": {:hex, :mimerl, "1.4.0", "3882a5ca67fbbe7117ba8947f27643557adec38fa2307490c4c4207624cb213b", [:rebar3], [], "hexpm", "13af15f9f68c65884ecca3a3891d50a7b57d82152792f3e19d88650aa126b144"},
"mint": {:hex, :mint, "1.9.0", "d6f534c2a3e98b2a8cc749b4796eb77e9e3af79a76f96e4c74035a827de0d318", [: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", "007154c7d8c43916aed3c93afd1f11aebbaa9c5ff4b7ba55ebe0d17ee0296042"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
"plug": {:hex, :plug, "1.19.1", "09bac17ae7a001a68ae393658aa23c7e38782be5c5c00c80be82901262c394c0", [: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", "560a0017a8f6d5d30146916862aaf9300b7280063651dd7e532b8be168511e62"},
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
"plug": {:hex, :plug, "1.19.2", "e4950525b22c6789dfb38a3f95d47171ba159da3fc5a33be9643b43d5e8adb98", [: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", "b6fce20a56af5e60fa5dfecf3f907bb98ec981be43c79a3809a499bc3d133de0"},
"plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
"req": {:hex, :req, "0.5.18", "48e6431cb4135e8a7815e745177485369a9b4a9924d5fe68ca00eb09ceaed1ef", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.21.0 or ~> 0.22.0", [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", "fa03812c440a9754bf34355e0c5d4f3ed316458db62e3284b7a352ef8dc0b996"},
"stream_data": {:hex, :stream_data, "1.3.0", "bde37905530aff386dea1ddd86ecbf00e6642dc074ceffc10b7d4e41dfd6aac9", [:mix], [], "hexpm", "3cc552e286e817dca43c98044c706eec9318083a1480c52ae2688b08e2936e3c"},
"sweet_xml": {:hex, :sweet_xml, "0.7.5", "803a563113981aaac202a1dbd39771562d0ad31004ddbfc9b5090bdcd5605277", [:mix], [], "hexpm", "193b28a9b12891cae351d81a0cead165ffe67df1b73fe5866d10629f4faefb12"},
"telemetry": {:hex, :telemetry, "1.4.2", "a0cb522801dffb1c49fe6e30561badffc7b6d0e180db1300df759faa22062855", [:rebar3], [], "hexpm", "928f6495066506077862c0d1646609eed891a4326bee3126ba54b60af61febb1"},
"thousand_island": {:hex, :thousand_island, "1.5.0", "f50a213cac97262b6d5ebb85745aa2c00fec1413191e6e66834788d45425cecb", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "708923d40523e43cf99041ab37a0d4b0ec426ac6438fa3716ab23d919eaeb412"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
"thousand_island": {:hex, :thousand_island, "1.4.3", "2158209580f633be38d43ec4e3ce0a01079592b9657afff9080d5d8ca149a3af", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6e4ce09b0fd761a58594d02814d40f77daff460c48a7354a15ab353bb998ea0b"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.1", "a48703a25c170eedadca83b11e88985af08d35f37c6f664d6dcfb106a97782fc", [:rebar3], [], "hexpm", "b3a917854ce3ae233619744ad1e0102e05673136776fb2fa76234f3e03b23642"},
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
}