Skip to content

Commit

Permalink
Improvements for Neo4j v5 engines
Browse files Browse the repository at this point in the history
  • Loading branch information
cichacz committed Dec 16, 2024
1 parent f71763b commit 35b174a
Show file tree
Hide file tree
Showing 20 changed files with 152 additions and 68 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/elixir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ jobs:

strategy:
matrix:
iex: [1.14.3]
otp: [24.3.4, 25.2]
iex: [1.14.5, 1.15.8, 1.16.3, 1.17.3]
otp: [24.3.4, 25.3, 26.2, 27.2]

steps:
- uses: actions/checkout@v3
Expand Down
4 changes: 2 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
elixir 1.14.0-otp-24
erlang 24.3.4
elixir 1.17.3
erlang 27.2
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,15 @@ Most popular engine for those is Neo4j thus this library focuses on providing fu

Currently only simple quering using raw Cypher queries is implemented, but there are few items on the Roadmap.

### Bolt_sips
## Existing libraries

One may say "there is already a library for communication with Neo4j". They are right **BUT** first and foremost, `bolt_sips` is left unmaintained ([discussion](https://github.com/florinpatrascu/bolt_sips/issues/109)). There were few attempts to continue that, but there is still no library that would take advantage of Elixir structs, protocols and behaviours to provide robust extensibility. Secondly, `bolt_sips` is just a driver. This library purpose will be to provide complete user experience when interacting with the DB.
There were already few attempts to write a driver for Bolt protocol but all of them seem to be clumsy in terms of protocol logic - many things are "hardcoded" as in the docs instead of being thought out for the server's operation and coding a reusable solution. They are not taking advantage of Elixir structs, protocols and behaviours to provide robust extensibility.
Secondly, those libs are just a driver and this library purpose is to provide complete user experience when interacting with the DB.
This should be solved by building Ecto-like support for the Cypher query language.

At this point, it's worth noting that this library may not be faster than `bolt_sips` or `boltx` due to greater usage of Protocols and structs.
You can modify tasks from `example_app` to benchmark those on your data.

## Installation

The package can be installed by adding `neo4ex` to your list of dependencies in `mix.exs`:
Expand Down
5 changes: 2 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
version: '3.1'

services:
graph_db:
image: neo4j:4.4.28-community
image: neo4j:5.26.0-community
environment:
NEO4J_AUTH: 'neo4j/letmein'
NEO4JLABS_PLUGINS: '["apoc"]'
NEO4J_dbms_security_auth__minimum__password__length: 6
ports:
- "7474:7474"
- "7687:7687"
Expand Down
11 changes: 7 additions & 4 deletions example_app/config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ config :example_app, ExampleApp.Connector,
credentials: "letmein",
pool_size: 1

config :bolt_sips, Bolt,
url: "bolt://localhost:7687",
basic_auth: [username: "neo4j", password: "letmein"],
config :boltx, Bolt,
uri: "bolt://localhost:7687",
auth: [username: "neo4j", password: "letmein"],
user_agent: "boltxTest/1",
pool_size: 1,
max_overflow: 0
max_overflow: 0,
prefix: :default,
name: Boltx
2 changes: 1 addition & 1 deletion example_app/lib/example_app/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ defmodule ExampleApp.Application do
def start(_type, _args) do
children = [
ExampleApp.Connector,
{Bolt.Sips, Application.get_env(:bolt_sips, Bolt)}
{Boltx, Application.get_env(:boltx, Bolt)}
]

# See https://hexdocs.pm/elixir/Supervisor.html
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
defmodule Mix.Tasks.ExampleApp.BoltSipsBenchmark do
defmodule Mix.Tasks.ExampleApp.BoltxBenchmark do
use Mix.Task

alias Neo4ex.Cypher

alias Bolt.Sips, as: Neo

alias ExampleApp.Connector

@requirements ["app.start"]

@shortdoc "Runs benchmark to compare with bolt_sips library"
@shortdoc "Runs benchmark to compare with boltx library"
def run(_args) do
Benchee.run(%{
"Neo4ex" => fn -> neo4ex() end,
"Bolt.Sips" => fn -> bolt_sips() end
"Boltx" => fn -> boltx() end
})
end

Expand All @@ -22,9 +20,9 @@ defmodule Mix.Tasks.ExampleApp.BoltSipsBenchmark do
Connector.run(%Cypher.Query{query: query, params: params})
end

def bolt_sips() do
def boltx() do
%{query: query, params: params} = customer_query()
Neo.query!(Neo.conn(), query, params)
Boltx.query!(Boltx, query, params)
end

def customer_query() do
Expand Down
2 changes: 1 addition & 1 deletion example_app/mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ defmodule ExampleApp.MixProject do
defp deps do
[
{:neo4ex, path: "../"},
{:bolt_sips, git: "https://github.com/florinpatrascu/bolt_sips", branch: "master"},
{:boltx, "~> 0.0.6"},
{:faker, "~> 0.17.0"},
{:jason, "~> 1.2"},
{:benchee, "~> 1.0"}
Expand Down
9 changes: 5 additions & 4 deletions example_app/mix.lock
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
%{
"benchee": {:hex, :benchee, "1.2.0", "afd2f0caec06ce3a70d9c91c514c0b58114636db9d83c2dc6bfd416656618353", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "ee729e53217898b8fd30aaad3cce61973dab61574ae6f48229fe7ff42d5e4457"},
"benchee": {:hex, :benchee, "1.3.1", "c786e6a76321121a44229dde3988fc772bca73ea75170a73fd5f4ddf1af95ccf", [:mix], [{:deep_merge, "~> 1.0", [hex: :deep_merge, repo: "hexpm", optional: false]}, {:statistex, "~> 1.0", [hex: :statistex, repo: "hexpm", optional: false]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "76224c58ea1d0391c8309a8ecbfe27d71062878f59bd41a390266bf4ac1cc56d"},
"bolt_sips": {:git, "https://github.com/florinpatrascu/bolt_sips", "b21901a46ed19b17d1c87a9ef9e56002f83f345c", [branch: "master"]},
"boltx": {:hex, :boltx, "0.0.6", "c6a396b1538b258e4d5ee2a94aaf8fb2c7879240efffba94b9159dbdce963790", [:mix], [{:db_connection, "~> 2.6.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "576b8f21a2021674130d04cd1fc79a4829a23d2cdf50641b3d7a00ce31b98ead"},
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
"db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"},
"db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"},
"deep_merge": {:hex, :deep_merge, "1.0.0", "b4aa1a0d1acac393bdf38b2291af38cb1d4a52806cf7a4906f718e1feb5ee961", [:mix], [], "hexpm", "ce708e5f094b9cd4e8f2be4f00d2f4250c4095be93f8cd6d018c753894885430"},
"faker": {:hex, :faker, "0.17.0", "671019d0652f63aefd8723b72167ecdb284baf7d47ad3a82a15e9b8a6df5d1fa", [:mix], [], "hexpm", "a7d4ad84a93fd25c5f5303510753789fc2433ff241bf3b4144d3f6f291658a6a"},
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"statistex": {:hex, :statistex, "1.0.0", "f3dc93f3c0c6c92e5f291704cf62b99b553253d7969e9a5fa713e5481cd858a5", [:mix], [], "hexpm", "ff9d8bee7035028ab4742ff52fc80a2aa35cece833cf5319009b52f1b5a86c27"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
}
19 changes: 11 additions & 8 deletions lib/neo4ex/bolt_protocol.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ defmodule Neo4ex.BoltProtocol do

alias Neo4ex.BoltProtocol.Structure.Message.Summary.{Success, Failure}

@user_agent "Neo4ex/#{Application.spec(:neo4ex, :vsn)}"

@impl true
def connect(opts) do
hostname = Keyword.get(opts, :hostname)
Expand All @@ -42,7 +40,11 @@ defmodule Neo4ex.BoltProtocol do
:ok <- hello(socket, opts) do
{:ok, socket}
else
other -> other
{:ok, %Failure{metadata: %{"message" => failure}}} ->
{:error, failure}

other ->
other
end
end

Expand Down Expand Up @@ -290,12 +292,14 @@ defmodule Neo4ex.BoltProtocol do
end

if Version.match?(bolt_version, ">= 5.1.0") do
hello = %Hello{extra: %Extra.Hello{user_agent: @user_agent}}
hello = %Hello{extra: %Extra.Hello{}}

logon = %Logon{
scheme: scheme,
principal: principal,
credentials: credentials
auth: %Extra.Logon{
scheme: scheme,
principal: principal,
credentials: credentials
}
}

with(
Expand All @@ -311,7 +315,6 @@ defmodule Neo4ex.BoltProtocol do
else
message = %Hello{
extra: %Extra.Hello{
user_agent: @user_agent,
scheme: scheme,
principal: principal,
credentials: credentials
Expand Down
14 changes: 13 additions & 1 deletion lib/neo4ex/bolt_protocol/structure/message/extra/hello.ex
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
defmodule Neo4ex.BoltProtocol.Structure.Message.Extra.Hello do
use Neo4ex.BoltProtocol.Structure

@version Mix.Project.config()[:version]
@system_info System.build_info()[:version]

# can't be encoded directly, it's just helper for the Hello message
embeded_structure do
field(:user_agent, default: "Neo4ex/0.1.0")
field(:user_agent, default: "Neo4ex/#{@version}")

field(:bolt_agent,
default: %{
product: "Neo4ex/#{@version}",
language: "Elixir/#{@system_info}"
},
version: ">= 5.3.0"
)

field(:patch_bolt, default: ["utc"], version: ">= 4.3.0 and <= 4.4.0")
field(:routing, default: %{}, version: ">= 4.1.0")

Expand Down
12 changes: 12 additions & 0 deletions lib/neo4ex/bolt_protocol/structure/message/extra/logon.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
defmodule Neo4ex.BoltProtocol.Structure.Message.Extra.Logon do
use Neo4ex.BoltProtocol.Structure

# TODO: implement validation
# @predefined_schemes ~w(none basic bearer kerberos)

embeded_structure do
field(:scheme, default: "")
field(:principal, default: "")
field(:credentials, default: "")
end
end
7 changes: 2 additions & 5 deletions lib/neo4ex/bolt_protocol/structure/message/request/logon.ex
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
defmodule Neo4ex.BoltProtocol.Structure.Message.Request.Logon do
use Neo4ex.BoltProtocol.Structure

# TODO: implement validation
# @predefined_schemes ~w(none basic bearer kerberos)
alias Neo4ex.BoltProtocol.Structure.Message.Extra

structure 0x6A do
field(:scheme, default: "")
field(:principal, default: "")
field(:credentials, default: "")
field(:auth, default: %Extra.Logon{})
end
end
9 changes: 6 additions & 3 deletions lib/neo4ex/connector.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ defmodule Neo4ex.Connector do
@noop <<0::size(@chunk_size)>>
# since 4.3 there is support for version range during negotiation
# so "4.4.1" actually means "4.4" plus one previous version "4.3"
@supported_versions ["4.4.1", "4.2.0", "4.1.0", "4.0.0"]
@supported_versions ["5.20.20", "4.4.3", "4.2.0", "4.0.0"]

defmacro __using__(otp_app: app) do
supported_versions = @supported_versions
Expand Down Expand Up @@ -101,14 +101,17 @@ defmodule Neo4ex.Connector do
end
end

def supported_versions() do
Enum.flat_map(@supported_versions, fn version ->
defmacro supported_versions() do
@supported_versions
|> Enum.flat_map(fn version ->
[major, minor, range] = version |> String.split(".") |> Enum.map(&String.to_integer/1)

for i <- minor..(minor - range) do
Version.parse!("#{major}.#{i}.0")
end
end)
|> Enum.uniq()
|> Macro.escape()
end

@doc false
Expand Down
4 changes: 3 additions & 1 deletion lib/neo4ex/utils.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
defmodule Neo4ex.Utils do
@moduledoc false

import Neo4ex.Connector, only: [supported_versions: 0]

alias Neo4ex.BoltProtocol

alias Neo4ex.PackStream
Expand Down Expand Up @@ -88,7 +90,7 @@ defmodule Neo4ex.Utils do
end

def list_valid_versions(requirement) do
Enum.filter(Neo4ex.Connector.supported_versions(), fn ver ->
Enum.filter(supported_versions(), fn ver ->
Version.match?(ver, requirement)
end)
end
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ defmodule Neo4ex.MixProject do
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:db_connection, "~> 2.4"},
{:db_connection, "~> 2.6.0"},

# Tests
{:mox, "~> 1.0", only: [:test]},
Expand Down
23 changes: 12 additions & 11 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@
"bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"},
"connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"},
"credo": {:hex, :credo, "1.6.7", "323f5734350fd23a456f2688b9430e7d517afb313fbd38671b8a4449798a7854", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "41e110bfb007f7eda7f897c10bf019ceab9a0b269ce79f015d54b0dcf4fc7dd3"},
"db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"},
"db_connection": {:hex, :db_connection, "2.6.0", "77d835c472b5b67fc4f29556dee74bf511bbafecdcaf98c27d27fa5918152086", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c2f992d15725e721ec7fbc1189d4ecdb8afef76648c746a8e1cad35e3b8a35f3"},
"dialyxir": {:hex, :dialyxir, "1.2.0", "58344b3e87c2e7095304c81a9ae65cb68b613e28340690dfe1a5597fd08dec37", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "61072136427a851674cab81762be4dbeae7679f85b1272b6d25c3a839aff8463"},
"earmark_parser": {:hex, :earmark_parser, "1.4.31", "a93921cdc6b9b869f519213d5bc79d9e218ba768d7270d46fdcf1c01bacff9e2", [:mix], [], "hexpm", "317d367ee0335ef037a87e46c91a2269fef6306413f731e8ec11fc45a7efd059"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},
"ex_doc": {:hex, :ex_doc, "0.29.4", "6257ecbb20c7396b1fe5accd55b7b0d23f44b6aa18017b415cb4c2b91d997729", [:mix], [{:earmark_parser, "~> 1.4.31", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2c6699a737ae46cb61e4ed012af931b57b699643b24dabe2400a8168414bc4f5"},
"earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"},
"erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
"ex_doc": {:hex, :ex_doc, "0.35.1", "de804c590d3df2d9d5b8aec77d758b00c814b356119b3d4455e4b8a8687aecaf", [:mix], [{:earmark_parser, "~> 1.4.39", [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", "2121c6402c8d44b05622677b761371a759143b958c6c19f6558ff64d0aed40df"},
"file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"},
"jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"},
"makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"},
"makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"},
"makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"},
"mox": {:hex, :mox, "1.0.2", "dc2057289ac478b35760ba74165b4b3f402f68803dd5aecd3bfd19c183815d64", [:mix], [], "hexpm", "f9864921b3aaf763c8741b5b8e6f908f44566f1e427b2630e89e9a73b981fef2"},
"nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"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.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"},
"mox": {:hex, :mox, "1.2.0", "a2cd96b4b80a3883e3100a221e8adc1b98e4c3a332a8fc434c39526babafd5b3", [:mix], [{:nimble_ownership, "~> 1.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}], "hexpm", "c7b92b3cc69ee24a7eeeaf944cd7be22013c52fcb580c1f33f50845ec821089a"},
"nimble_ownership": {:hex, :nimble_ownership, "1.0.1", "f69fae0cdd451b1614364013544e66e4f5d25f36a2056a9698b793305c5aa3a6", [:mix], [], "hexpm", "3825e461025464f519f3f3e4a1f9b68c47dc151369611629ad08b636b73bb22d"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
}
Loading

0 comments on commit 35b174a

Please sign in to comment.