From a659ff46939c1ee1a7ee37c3e815b37afe4bc26d Mon Sep 17 00:00:00 2001 From: Zach Daniel Date: Thu, 18 Jul 2024 13:24:21 -0400 Subject: [PATCH] improvement: add extension installation code --- .tool-versions | 4 +- lib/domain/domain.ex | 52 ++++++++ lib/igniter.ex | 178 +++++++++++++++++++++++++++ lib/mix/tasks/ash_graphql.install.ex | 129 +------------------ lib/resource/igniter.ex | 0 lib/resource/resource.ex | 28 +++++ mix.exs | 4 +- mix.lock | 10 +- 8 files changed, 271 insertions(+), 134 deletions(-) create mode 100644 lib/igniter.ex create mode 100644 lib/resource/igniter.ex diff --git a/.tool-versions b/.tool-versions index 0a06d5a..ce0360e 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -erlang 26.0.2 -elixir 1.16.0 +erlang 27.0.1 +elixir 1.17.2-otp-27 diff --git a/lib/domain/domain.ex b/lib/domain/domain.ex index b74bc9d..8bdcfad 100644 --- a/lib/domain/domain.ex +++ b/lib/domain/domain.ex @@ -122,6 +122,58 @@ defmodule AshGraphql.Domain do AshGraphql.Domain.Transformers.ValidateCompatibleNames ] + def install(igniter, module, Ash.Domain, _path, _argv) do + igniter + |> Spark.Igniter.add_extension( + module, + Ash.Domain, + :extensions, + AshGraphql.Domain + ) + |> add_to_graphql_schema(module) + end + + defp add_to_graphql_schema(igniter, domain) do + case AshGraphql.Igniter.find_schema(igniter, domain) do + {:ok, igniter, _} -> + igniter + + {:error, igniter, []} -> + AshGraphql.Igniter.setup_absinthe_schema(igniter) + + {:error, igniter, all_schemas} -> + schema = + case all_schemas do + [schema] -> + schema + + schemas -> + Owl.IO.select( + schemas, + label: "Multiple Ash.Graphql modules found. Please select one to use:", + render_as: &inspect/1 + ) + end + + Igniter.Code.Module.find_and_update_module!(igniter, schema, fn zipper -> + with {:ok, zipper} <- Igniter.Code.Module.move_to_use(zipper, AshGraphql), + {:ok, zipper} <- Igniter.Code.Function.move_to_nth_argument(zipper, 1), + {:ok, zipper} <- Igniter.Code.Keyword.get_key(zipper, :domains), + {:ok, zipper} <- Igniter.Code.List.append_to_list(zipper, domain) do + {:ok, zipper} + else + _ -> + {:warning, + """ + Could not add #{inspect(domain)} to the list of domains in #{inspect(schema)}. + + Please make that change manually. + """} + end + end) + end + end + @deprecated "See `AshGraphql.Domain.Info.authorize?/1`" defdelegate authorize?(domain), to: AshGraphql.Domain.Info diff --git a/lib/igniter.ex b/lib/igniter.ex new file mode 100644 index 0000000..59e27fe --- /dev/null +++ b/lib/igniter.ex @@ -0,0 +1,178 @@ +defmodule AshGraphql.Igniter do + @moduledoc "Codemods and utilities for working with AshGraphql & Igniter" + + @doc "Returns the AshGraphql schema containing the domain in question, or a list of all AshGraphql schemas" + def find_schema(igniter, domain) do + {igniter, modules} = ash_graphql_schemas(igniter) + + modules + |> Enum.find(fn module -> + with {:ok, {_igniter, _source, zipper}} <- Igniter.Code.Module.find_module(igniter, module), + {:ok, zipper} <- Igniter.Code.Module.move_to_use(zipper, AshGraphql), + {:ok, zipper} <- Igniter.Code.Function.move_to_nth_argument(zipper, 1), + {:ok, zipper} <- Igniter.Code.Keyword.get_key(zipper, :domains), + {:ok, _zipper} <- + Igniter.Code.List.move_to_list_item( + zipper, + &Igniter.Code.Common.nodes_equal?(&1, domain) + ) do + true + else + _ -> + false + end + end) + |> case do + nil -> + {:error, igniter, modules} + + module -> + {:ok, igniter, module} + end + end + + @doc "Sets up an absinthe schema for AshGraphql" + def setup_absinthe_schema(igniter, schema_name \\ nil) do + schema_name = schema_name || Igniter.Code.Module.module_name("GraphqlSchema") + + {igniter, domains} = Ash.Domain.Igniter.list_domains(igniter) + + {igniter, domains} = + Enum.reduce(domains, {igniter, []}, fn domain, {igniter, list} -> + case Spark.Igniter.has_extension( + igniter, + domain, + Ash.Domain, + :extensions, + AshGraphql.Domain + ) do + {igniter, true} -> {igniter, [domain | list]} + {igniter, false} -> {igniter, list} + end + end) + + {igniter, resources} = Ash.Resource.Igniter.list_resources(igniter) + + {igniter, any_queries?} = + Enum.reduce_while( + Enum.map(resources, &{&1, [2, 3]}) ++ Enum.map(domains, &{&1, [3, 4]}), + {igniter, false}, + fn {mod, arities}, {igniter, false} -> + with {:ok, {igniter, _source, zipper}} <- + Igniter.Code.Module.find_module(igniter, mod), + {:ok, zipper} <- + Igniter.Code.Function.move_to_function_call_in_current_scope( + zipper, + :graphql, + 1 + ), + {:ok, zipper} <- Igniter.Code.Common.move_to_do_block(zipper), + {:ok, zipper} <- + Igniter.Code.Function.move_to_function_call_in_current_scope( + zipper, + :queries, + 1 + ), + {:ok, zipper} <- Igniter.Code.Common.move_to_do_block(zipper) do + has_query? = + Enum.any?([:get, :read_one, :list, :action], fn query_name -> + match?( + {:ok, _}, + Igniter.Code.Function.move_to_function_call_in_current_scope( + zipper, + query_name, + arities + ) + ) + end) + + if has_query? do + {:halt, {igniter, true}} + else + {:cont, {igniter, false}} + end + else + _ -> + {:cont, {igniter, false}} + end + end + ) + + placeholder_query = + unless any_queries? do + """ + @desc "Remove me once you have a query of your own!" + field :remove_me, :string do + resolve fn _, _, _ -> + {:ok, "Remove me!"} + end + end + """ + end + + igniter + |> Igniter.Code.Module.find_and_update_or_create_module( + schema_name, + """ + use Absinthe.Schema + use AshGraphql, domains: #{inspect(domains)} + + query do + # Custom absinthe queries can be placed here + #{placeholder_query} + end + + mutation do + # Custom absinthe mutations can be placed here + end + """, + fn zipper -> + # Should never get here + {:ok, zipper} + end + ) + end + + @doc "Sets up the phoenix module for AshGraphql" + def setup_phoenix(igniter, schema_name \\ nil) do + schema_name = schema_name || Igniter.Code.Module.module_name("GraphqlSchema") + + case Igniter.Libs.Phoenix.select_router(igniter) do + {igniter, nil} -> + igniter + |> Igniter.add_warning(""" + No Phoenix router found, skipping phoenix installation. + + See the getting started guide for instructions on installing AshGraphql with `plug`. + If you have yet to set up phoenix, you'll have to do that manually and then rerun this installer. + """) + + {igniter, router} -> + igniter + |> Igniter.Libs.Phoenix.add_pipeline(:graphql, "plug AshGraphql.Plug", router: router) + |> Igniter.Libs.Phoenix.add_scope( + "/gql", + """ + pipe_through [:graphql] + + forward "/playground", + Absinthe.Plug.GraphiQL, + schema: Module.concat(["#{inspect(schema_name)}"]), + interface: :playground + + forward "/", + Absinthe.Plug, + schema: Module.concat(["#{inspect(schema_name)}"]) + """, + router: router + ) + end + end + + @doc "Returns all modules that `use AshGraphql`" + def ash_graphql_schemas(igniter) do + Igniter.Code.Module.find_all_matching_modules(igniter, fn _name, zipper -> + match?({:ok, _}, Igniter.Code.Module.move_to_use(zipper, AshGraphql)) + end) + end +end diff --git a/lib/mix/tasks/ash_graphql.install.ex b/lib/mix/tasks/ash_graphql.install.ex index ddd5051..9bd1dd8 100644 --- a/lib/mix/tasks/ash_graphql.install.ex +++ b/lib/mix/tasks/ash_graphql.install.ex @@ -14,138 +14,15 @@ defmodule Mix.Tasks.AshGraphql.Install do schema_name = Igniter.Code.Module.module_name("GraphqlSchema") {igniter, candidate_ash_graphql_schemas} = - Igniter.Code.Module.find_all_matching_modules(igniter, fn _name, zipper -> - zipper - |> Igniter.Code.Module.move_to_use(AshGraphql) - end) + AshGraphql.Igniter.ash_graphql_schemas(igniter) if Enum.empty?(candidate_ash_graphql_schemas) do igniter - |> setup_absinthe_schema(schema_name) - |> setup_web(schema_name) + |> AshGraphql.Igniter.setup_absinthe_schema(schema_name) + |> AshGraphql.Igniter.setup_phoenix(schema_name) else igniter |> Igniter.add_warning("AshGraphql schema already exists, skipping installation.") end end - - defp setup_web(igniter, schema_name) do - case Igniter.Libs.Phoenix.select_router(igniter) do - {igniter, nil} -> - igniter - |> Igniter.add_warning(""" - No Phoenix router found, skipping Phoenix installation. - - See the Getting Started guide for instructions on installing AshGraphql with `plug`. - If you have yet to set up Phoenix, you'll have to do that manually and then rerun this installer. - """) - - {igniter, router} -> - igniter - |> Igniter.Libs.Phoenix.add_pipeline(:graphql, "plug AshGraphql.Plug", router: router) - |> Igniter.Libs.Phoenix.add_scope( - "/gql", - """ - pipe_through [:graphql] - - forward "/playground", - Absinthe.Plug.GraphiQL, - schema: Module.concat(["#{inspect(schema_name)}"]), - interface: :playground - - forward "/", - Absinthe.Plug, - schema: Module.concat(["#{inspect(schema_name)}"]) - """, - router: router - ) - end - end - - defp setup_absinthe_schema(igniter, schema_name) do - {igniter, domains} = Ash.Domain.Igniter.list_domains(igniter) - {igniter, resources} = Ash.Resource.Igniter.list_resources(igniter) - - {igniter, any_queries?} = - Enum.reduce_while( - Enum.map(resources, &{&1, [2, 3]}) ++ Enum.map(domains, &{&1, [3, 4]}), - {igniter, false}, - fn {mod, arities}, {igniter, false} -> - with {:ok, {igniter, _source, zipper}} <- - Igniter.Code.Module.find_module(igniter, mod), - {:ok, zipper} <- - Igniter.Code.Function.move_to_function_call_in_current_scope( - zipper, - :graphql, - 1 - ), - {:ok, zipper} <- Igniter.Code.Common.move_to_do_block(zipper), - {:ok, zipper} <- - Igniter.Code.Function.move_to_function_call_in_current_scope( - zipper, - :queries, - 1 - ), - {:ok, zipper} <- Igniter.Code.Common.move_to_do_block(zipper) do - has_query? = - Enum.any?([:get, :read_one, :list, :action], fn query_name -> - match?( - {:ok, _}, - Igniter.Code.Function.move_to_function_call_in_current_scope( - zipper, - query_name, - arities - ) - ) - end) - - if has_query? do - {:halt, {igniter, true}} - else - {:cont, {igniter, false}} - end - else - _ -> - {:cont, {igniter, false}} - end - end - ) - - placeholder_query = - unless any_queries? do - ~S''' - @desc """ - Hello! This is a sample query to verify that AshGraphql has been set up correctly. - Remove me once you have a query of your own! - """ - field :say_hello, :string do - resolve fn _, _, _ -> - {:ok, "Hello from AshGraphql!"} - end - end - ''' - end - - igniter - |> Igniter.Code.Module.find_and_update_or_create_module( - schema_name, - """ - use Absinthe.Schema - use AshGraphql, domains: #{inspect(domains)} - - query do - # Custom Absinthe queries can be placed here - #{placeholder_query} - end - - mutation do - # Custom Absinthe mutations can be placed here - end - """, - fn zipper -> - # Should never get here - {:ok, zipper} - end - ) - end end diff --git a/lib/resource/igniter.ex b/lib/resource/igniter.ex new file mode 100644 index 0000000..e69de29 diff --git a/lib/resource/resource.ex b/lib/resource/resource.ex index 407c8ef..cfcb314 100644 --- a/lib/resource/resource.ex +++ b/lib/resource/resource.ex @@ -452,6 +452,34 @@ defmodule AshGraphql.Resource do %{module: __MODULE__, location: %{file: env.file, line: env.line}} end + # sobelow_skip ["DOS.StringToAtom"] + def install(igniter, module, Ash.Resource, _path, _argv) do + type = + module + |> Module.split() + |> List.last() + |> Macro.underscore() + |> String.to_atom() + + igniter = + case Ash.Resource.Igniter.domain(igniter, module) do + {:ok, igniter, domain} -> + AshGraphql.Domain.install(igniter, domain, Ash.Domain, nil, nil) + + {:error, igniter} -> + igniter + end + + igniter + |> Spark.Igniter.add_extension( + module, + Ash.Resource, + :extensions, + AshGraphql.Resource + ) + |> Spark.Igniter.set_option(module, [:graphql, :type], type) + end + def encode_id(record, relay_ids?) do if relay_ids? do encode_relay_id(record) diff --git a/mix.exs b/mix.exs index c010191..b7d7f93 100644 --- a/mix.exs +++ b/mix.exs @@ -138,12 +138,12 @@ defmodule AshGraphql.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do [ - {:ash, ash_version("~> 3.0 and >= 3.1.3")}, + {:ash, ash_version("~> 3.0 and >= 3.2.3")}, {:absinthe_plug, "~> 1.4"}, {:absinthe, "~> 1.7"}, {:jason, "~> 1.2"}, {:igniter, "~> 0.3"}, - {:spark, "~> 2.2 and >= 2.2.8"}, + {:spark, "~> 2.2 and >= 2.2.10"}, # dev/test dependencies {:ex_doc, github: "elixir-lang/ex_doc", only: [:dev, :test], runtime: false}, {:ex_check, "~> 0.12", only: [:dev, :test]}, diff --git a/mix.lock b/mix.lock index ba31167..dea77ba 100644 --- a/mix.lock +++ b/mix.lock @@ -1,7 +1,7 @@ %{ "absinthe": {:hex, :absinthe, "1.7.7", "ecbf4e9b21372dda271c79bb43dded3583b4f080348c5e68d9b5445e790ff17e", [:mix], [{:dataloader, "~> 1.0.0 or ~> 2.0", [hex: :dataloader, repo: "hexpm", optional: true]}, {:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}, {:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}, {:opentelemetry_process_propagator, "~> 0.2.1 or ~> 0.3", [hex: :opentelemetry_process_propagator, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2145519828bcb7c8621b72d7af2bcff88b01cba2774583c40ebd867e1d336ff6"}, "absinthe_plug": {:hex, :absinthe_plug, "1.5.8", "38d230641ba9dca8f72f1fed2dfc8abd53b3907d1996363da32434ab6ee5d6ab", [:mix], [{:absinthe, "~> 1.5", [hex: :absinthe, repo: "hexpm", optional: false]}, {:plug, "~> 1.4", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "bbb04176647b735828861e7b2705465e53e2cf54ccf5a73ddd1ebd855f996e5a"}, - "ash": {:hex, :ash, "3.2.2", "f079fbe7f4f7e3279825c841aa69f6b83333a86267b31435ca97031a805c4b41", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.2.12 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, ">= 0.8.1 and < 1.0.0-0", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.2.8 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b9a9165d1aafec1fa719df6182ddee0b2a29c83f67079a04a9a8060f49cc1e7f"}, + "ash": {:hex, :ash, "3.2.3", "41cf9fba19f2b86dfac162210ee8676c1def4dd0d89be4fd0eb61cf2f7efbcb7", [:mix], [{:comparable, "~> 1.0", [hex: :comparable, repo: "hexpm", optional: false]}, {:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.2.12 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, ">= 0.8.1 and < 1.0.0-0", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.2.8 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "70464cc6eb5584750ca6b6e2ea8974b31e4a9818525581239ccfedc39f3e9d95"}, "bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"}, "comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"}, "credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [: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", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"}, @@ -17,7 +17,9 @@ "git_cli": {:hex, :git_cli, "0.3.0", "a5422f9b95c99483385b976f5d43f7e8233283a47cda13533d7c16131cb14df5", [:mix], [], "hexpm", "78cb952f4c86a41f4d3511f1d3ecb28edb268e3a7df278de2faa1bd4672eaf9b"}, "git_ops": {:hex, :git_ops, "2.6.1", "cc7799a68c26cf814d6d1a5121415b4f5bf813de200908f930b27a2f1fe9dad5", [:mix], [{:git_cli, "~> 0.2", [hex: :git_cli, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "ce62d07e41fe993ec22c35d5edb11cf333a21ddaead6f5d9868fcb607d42039e"}, "glob_ex": {:hex, :glob_ex, "0.1.7", "eae6b6377147fb712ac45b360e6dbba00346689a87f996672fe07e97d70597b1", [:mix], [], "hexpm", "decc1c21c0c73df3c9c994412716345c1692477b9470e337f628a7e08da0da6a"}, - "igniter": {:hex, :igniter, "0.3.2", "f1a60895c0ee40543efd8f8c57f3eb07373e97320e7673272589e611f3f8ac30", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:owl, "~> 0.9", [hex: :owl, repo: "hexpm", optional: false]}, {:rewrite, "~> 0.9", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}, {:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: false]}], "hexpm", "b8ffae7e6860ffdc0abe89d0cf4104c110bf1296748600de1e5744e03ff2e663"}, + "igniter": {:hex, :igniter, "0.3.3", "5cfc2c8b26df4c390d0238838ed9b993c1d0e12c4cae7e83e8ffbce342c7d3da", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:inflex, "~> 2.0", [hex: :inflex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:owl, "~> 0.9", [hex: :owl, repo: "hexpm", optional: false]}, {:rewrite, "~> 0.9", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}, {:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: false]}], "hexpm", "766429591d12022960ce358b019b11c2bf0fac172d3b09af2f848aabb86d8b95"}, + "inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"}, + "iterex": {:hex, :iterex, "0.1.1", "90378a9561ad87da46737dceaf02e68a0b3023746216a4de34a0c509f5f505d4", [:mix], [], "hexpm", "c4f5916a6dbb03aa4c3d5c480069e13075ca6a57bd0c28d643da3891962440ad"}, "jason": {:hex, :jason, "1.4.3", "d3f984eeb96fe53b85d20e0b049f03e57d075b5acda3ac8d465c969a2536c17b", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3"}, "libgraph": {:hex, :libgraph, "0.16.0", "3936f3eca6ef826e08880230f806bfea13193e49bf153f93edcf0239d4fd1d07", [:mix], [], "hexpm", "41ca92240e8a4138c30a7e06466acc709b0cbb795c643e9e17174a178982d6bf"}, "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, @@ -31,12 +33,12 @@ "owl": {:hex, :owl, "0.9.0", "9b33d64734bd51d3fc1d6ed01b12f8c2ed23e1fbf8c43658a6dfbff62578bd03", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "cd70b55327985f8f24d38cb7de5bf8a8d24040e1b49cca2345508f8119ce81fd"}, "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"}, - "reactor": {:hex, :reactor, "0.8.5", "7a621e0392a5975ed97938a4ddbbc92a6a31157fbd87446bc8bc6b1a0f49e56a", [:mix], [{:igniter, "~> 0.2", [hex: :igniter, repo: "hexpm", optional: false]}, {:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "17b1976b9d333e55382dc108779078d5bbdbcd2c3d4033ea6dd52437339fe469"}, + "reactor": {:hex, :reactor, "0.9.0", "f48af9f300454b979a22d5a04b18b59e16959478ffa7f88d50b5e142b5d055dc", [:mix], [{:igniter, "~> 0.2", [hex: :igniter, repo: "hexpm", optional: false]}, {:iterex, "~> 0.1", [hex: :iterex, repo: "hexpm", optional: false]}, {:libgraph, "~> 0.16", [hex: :libgraph, repo: "hexpm", optional: false]}, {:spark, "~> 2.0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.2", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4c5ffd700ac669d0992a9e296978abe2110670b23addc0970fca9108d506489c"}, "rewrite": {:hex, :rewrite, "0.10.5", "6afadeae0b9d843b27ac6225e88e165884875e0aed333ef4ad3bf36f9c101bed", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "51cc347a4269ad3a1e7a2c4122dbac9198302b082f5615964358b4635ebf3d4f"}, "simple_sat": {:hex, :simple_sat, "0.1.3", "f650fc3c184a5fe741868b5ac56dc77fdbb428468f6dbf1978e14d0334497578", [:mix], [], "hexpm", "a54305066a356b7194dc81db2a89232bacdc0b3edaef68ed9aba28dcbc34887b"}, "sobelow": {:hex, :sobelow, "0.13.0", "218afe9075904793f5c64b8837cc356e493d88fddde126a463839351870b8d1e", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "cd6e9026b85fc35d7529da14f95e85a078d9dd1907a9097b3ba6ac7ebbe34a0d"}, "sourceror": {:hex, :sourceror, "1.4.0", "be87319b1579191e25464005d465713079b3fd7124a3938a1e6cf4def39735a9", [:mix], [], "hexpm", "16751ca55e3895f2228938b703ad399b0b27acfe288eff6c0e629ed3e6ec0358"}, - "spark": {:hex, :spark, "2.2.9", "cc86e39895e1e1b2360e333fe37fa8cdb5624d265d234c0c945b1b1e11b49563", [:mix], [{:igniter, ">= 0.2.6 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "d855e3971f568527bdd43377a8088ae96a7798a1df732c9b7a631730ba2f705d"}, + "spark": {:hex, :spark, "2.2.10", "834c5f6c6874d019116096b82fe8a3e9bfe92077c3e06ead14b6daff528b69ef", [:mix], [{:igniter, ">= 0.2.6 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.2", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "78972edb0cc1539e56d42f08aabc88b8b2892d64e3e8bd44d58113b7f63622fa"}, "spitfire": {:hex, :spitfire, "0.1.3", "7ea0f544005dfbe48e615ed90250c9a271bfe126914012023fd5e4b6b82b7ec7", [:mix], [], "hexpm", "d53b5107bcff526a05c5bb54c95e77b36834550affd5830c9f58760e8c543657"}, "splode": {:hex, :splode, "0.2.4", "71046334c39605095ca4bed5d008372e56454060997da14f9868534c17b84b53", [:mix], [], "hexpm", "ca3b95f0d8d4b482b5357954fec857abd0fa3ea509d623334c1328e7382044c2"}, "stream_data": {:hex, :stream_data, "1.1.1", "fd515ca95619cca83ba08b20f5e814aaf1e5ebff114659dc9731f966c9226246", [:mix], [], "hexpm", "45d0cd46bd06738463fd53f22b70042dbb58c384bb99ef4e7576e7bb7d3b8c8c"},