This commit is contained in:
Zach Daniel 2022-04-08 02:59:39 -04:00
parent 86798987d4
commit 4e219667f2
17 changed files with 417 additions and 115 deletions

View file

@ -1,3 +1,7 @@
defmodule AshHq.Docs do
use Ash.Api, otp_app: :ash_hq
execution do
timeout 30_000
end
end

View file

@ -93,6 +93,7 @@ defmodule AshHq.Docs.Importer do
end
LibraryVersion.build!(library.id, version, result, %{
timeout: :infinity,
id: id,
doc: result[:doc],
guides: result[:guides],
@ -105,7 +106,7 @@ defmodule AshHq.Docs.Importer do
for version <- LibraryVersion.unprocessed!() do
try do
LibraryVersion.process!(version)
LibraryVersion.process!(version, timeout: :infinity)
rescue
e ->
Logger.error(

View file

@ -13,7 +13,8 @@ defmodule AshHq.Docs.Dsl do
:extension_type,
:extension_name,
:version_name,
:library_name
:library_name,
:library_id
]
end
@ -99,6 +100,7 @@ defmodule AshHq.Docs.Dsl do
first :extension_name, :extension, :name
first :version_name, :library_version, :version
first :library_name, [:library_version, :library], :name
first :library_id, [:library_version, :library], :id
end
relationships do

View file

@ -12,7 +12,8 @@ defmodule AshHq.Docs.Function do
load_for_search([
:version_name,
:library_name,
:module_name
:module_name,
:library_id
])
type "Code"
@ -81,6 +82,7 @@ defmodule AshHq.Docs.Function do
aggregates do
first :version_name, :library_version, :version
first :library_name, [:library_version, :library], :name
first :library_id, [:library_version, :library], :id
first :module_name, :module, :name
end

View file

@ -3,7 +3,8 @@ defmodule AshHq.Docs.Guide.Changes.SetRoute do
def change(changeset, _, _) do
if !Ash.Changeset.get_attribute(changeset, :route) &&
(Ash.Changeset.changing_attribute?(:name) || Ash.Changeset.changing_attribute?(:category)) do
(Ash.Changeset.changing_attribute?(changeset, :name) ||
Ash.Changeset.changing_attribute?(changeset, :category)) do
category = Ash.Changeset.get_attribute(changeset, :category)
name = Ash.Changeset.get_attribute(changeset, :name)
Ash.Changeset.change_attribute(changeset, :route, to_path(category) <> "/" <> to_path(name))

View file

@ -10,7 +10,8 @@ defmodule AshHq.Docs.Module do
search do
load_for_search [
:version_name,
:library_name
:library_name,
:library_id
]
type "Code"
@ -69,6 +70,7 @@ defmodule AshHq.Docs.Module do
aggregates do
first :version_name, :library_version, :version
first :library_name, [:library_version, :library], :name
first :library_id, [:library_version, :library], :id
end
relationships do

View file

@ -13,7 +13,8 @@ defmodule AshHq.Docs.Option do
:extension_type,
:extension_name,
:version_name,
:library_name
:library_name,
:library_id
]
end
@ -85,6 +86,7 @@ defmodule AshHq.Docs.Option do
first :extension_order, [:dsl, :extension], :order
first :version_name, :library_version, :version
first :library_name, [:library_version, :library], :name
first :library_id, [:library_version, :library], :id
end
relationships do

View file

@ -20,7 +20,7 @@ defmodule AshHqWeb.Components.DocSidebar do
def render(assigns) do
~F"""
<aside id={@id} class={"grid w-64 h-full overflow-y-scroll", @class} aria-label="Sidebar">
<aside id={@id} class={"grid w-64 h-full overflow-y-scroll pb-36", @class} aria-label="Sidebar">
<div class="py-3 px-3">
<ul class="space-y-2">
{#for library <- @libraries}
@ -44,11 +44,7 @@ defmodule AshHqWeb.Components.DocSidebar do
{#for guide <- guides}
<li class="ml-3">
<LivePatch
to={Routes.guide_link(
library,
selected_version_name(library, @selected_versions),
guide.route
)}
to={Routes.doc_link(guide, @selected_versions)}
class={
"flex items-center p-1 text-base font-normal text-gray-900 rounded-lg dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700",
"dark:bg-gray-600": @guide && @guide.id == guide.id
@ -69,7 +65,7 @@ defmodule AshHqWeb.Components.DocSidebar do
{#for extension <- get_extensions(library, @selected_versions)}
<li class="ml-3">
<LivePatch
to={Routes.extension_link(library, selected_version_name(library, @selected_versions), extension.name)}
to={Routes.doc_link(extension, @selected_versions)}
class={
"flex items-center p-1 text-base font-normal text-gray-900 rounded-lg dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700",
"dark:bg-gray-600": @extension && @extension.id == extension.id
@ -90,7 +86,7 @@ defmodule AshHqWeb.Components.DocSidebar do
{#for module <- @library_version.modules}
<li class="ml-3">
<LivePatch
to={Routes.module_link(library, @library_version.version, module.name)}
to={Routes.doc_link(module, @selected_versions)}
class={
"flex items-center pt-1 text-base font-normal text-gray-900 rounded-lg dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700",
"dark:bg-gray-600": @module && @module.id == module.id
@ -131,7 +127,7 @@ defmodule AshHqWeb.Components.DocSidebar do
{/if}
{/if}
<LivePatch
to={Routes.dsl_link(@library, @library_version.version, @extension.name, dsl)}
to={Routes.doc_link(dsl, @selected_versions)}
class={
"flex items-center p-1 text-base font-normal rounded-lg hover:text-orange-300",
"text-orange-600 dark:text-orange-400 font-bold": @dsl && @dsl.id == dsl.id
@ -204,12 +200,16 @@ defmodule AshHqWeb.Components.DocSidebar do
end
defp selected_version_name(library, selected_versions) do
case Enum.find(library.versions, &(&1.id == selected_versions[library.id])) do
nil ->
nil
if selected_versions[library.id] == "latest" do
"latest"
else
case Enum.find(library.versions, &(&1.id == selected_versions[library.id])) do
nil ->
nil
version ->
version.version
version ->
version.version
end
end
end

View file

@ -79,7 +79,7 @@ defmodule AshHqWeb.Components.Search do
class="text-black form-select rounded-md pt-1 py-2 w-3/4 border dark:border-0 bg-gray-100 dark:bg-white"
name={"versions[#{library.id}]"}
selected={Map.get(@selected_versions, library.id)}
options={Enum.map(library.versions, &{&1.version, &1.id})}
options={[{"latest", "latest"}] ++ Enum.map(library.versions, &{&1.version, &1.id})}
/>
</div>
</div>
@ -134,7 +134,7 @@ defmodule AshHqWeb.Components.Search do
{/if}
</div>
{#for item <- results.items}
<LiveRedirect to={Routes.doc_link(item)} opts={id: item.id}>
<LiveRedirect to={Routes.doc_link(item, @selected_versions)} opts={id: item.id}>
<div class={
"rounded-lg mb-4 py-4 px-2 hover:bg-gray-400 dark:hover:bg-gray-600",
"bg-gray-400 dark:bg-gray-600": @selected_item.id == item.id,
@ -213,10 +213,31 @@ defmodule AshHqWeb.Components.Search do
if socket.assigns[:search] in [nil, ""] || socket.assigns[:selected_versions] in [nil, %{}] do
assign(socket, :results, %{})
else
versions =
Enum.map(socket.assigns.selected_versions, fn
{library_id, "latest"} ->
Enum.find_value(socket.assigns.libraries, fn library ->
if library.id == library_id do
case Enum.find(library.versions, &String.contains?(&1.version, ".")) ||
Enum.at(library.versions, 0) do
nil ->
nil
version ->
version.id
end
end
end)
{_, version_id} ->
version_id
end)
|> Enum.reject(&is_nil/1)
%{results: results, item_list: item_list} =
AshHq.Docs.Search.run!(
socket.assigns.search,
Map.values(socket.assigns.selected_versions)
versions
)
selected_item = Enum.at(item_list, 0)

View file

@ -54,7 +54,7 @@ defmodule AshHqWeb.Pages.Docs do
</div>
{/if}
</div>
<span class="grid overflow-hidden lg:hidden">
<span class="grid overflow-hidden xl:hidden">
<div
id="mobile-sidebar-container"
class="overflow-hidden hidden fixed w-min h-full transition bg-white dark:bg-primary-black"
@ -322,8 +322,8 @@ defmodule AshHqWeb.Pages.Docs do
end
defp assign_extension(socket) do
if socket.assigns.library && socket.assigns[:params]["extension"] do
extensions = get_extensions(socket.assigns.library, socket.assigns.selected_versions)
if socket.assigns.library_version && socket.assigns[:params]["extension"] do
extensions = socket.assigns.library_version.extensions
assign(socket,
extension:
@ -340,16 +340,6 @@ defmodule AshHqWeb.Pages.Docs do
{:ok, socket}
end
defp get_extensions(library, selected_versions) do
case Enum.find(library.versions, &(&1.id == selected_versions[library.id])) do
nil ->
[]
version ->
version.extensions
end
end
defp assign_library(socket) do
if !socket.assigns[:library] ||
socket.assigns.params["library"] != Routes.sanitize_name(socket.assigns.library.name) do
@ -364,10 +354,17 @@ defmodule AshHqWeb.Pages.Docs do
socket =
if socket.assigns[:params]["version"] do
library_version =
Enum.find(
library.versions,
&(Routes.sanitize_name(&1.version) == socket.assigns[:params]["version"])
)
case socket.assigns[:params]["version"] do
"latest" ->
Enum.find(library.versions, &String.contains?(&1.version, ".")) ||
Enum.at(library.versions, 0)
version ->
Enum.find(
library.versions,
&(Routes.sanitize_name(&1.version) == version)
)
end
if library_version do
new_selected_versions =

View file

@ -2,6 +2,9 @@ defmodule AshHqWeb.Pages.Home do
use Surface.LiveComponent
alias AshHqWeb.Components.{CalloutText, CodeExample, SearchBar}
alias Surface.Components.LiveRedirect
prop libraries, :list, default: []
def render(assigns) do
~F"""
@ -15,6 +18,23 @@ defmodule AshHqWeb.Pages.Home do
or build your own extensions. Plug-and-play with other excellent tools like <CalloutText>Phoenix</CalloutText> and <CalloutText>Absinthe</CalloutText>.
</div>
<SearchBar class="hidden sm:block mt-16" />
<div class="grid grid-cols-3 w-full place-content-evenly mt-12">
<div class="grid grid-cols-1 text-center">
Data Layers
<LiveRedirect to={"/"}>
AshPostgres
</LiveRedirect>
</div>
<div class="grid grid-cols-1 text-center">
APIs
</div>
<div class="grid grid-cols-1 text-center">
Build your own
</div>
</div>
<div class="pt-6 pb-6 w-full hidden sm:block mt-36">
<div class="grid grid-cols-1 lg:grid-cols-2 gap-10">
<CodeExample

View file

@ -1,78 +1,95 @@
defmodule AshHqWeb.Routes do
def guide_link(library, version, route) do
"/docs/guides/#{sanitize_name(library.name)}/#{sanitize_name(version)}/#{route}"
end
def library_link(library, nil) do
"/docs/dsl/#{sanitize_name(library.name)}"
end
def library_link(library, name) do
"/docs/dsl/#{sanitize_name(library.name)}/#{sanitize_name(name)}"
end
def extension_link(library, name, extension) do
"/docs/dsl/#{sanitize_name(library.name)}/#{sanitize_name(name)}/#{sanitize_name(extension)}"
def doc_link(entity, selected_versions \\ %{})
def doc_link(%AshHq.Docs.Library{name: name}, _selected_version) do
"/docs/dsl/#{sanitize_name(name)}"
end
def dsl_link(library, name, extension, item) do
def doc_link(
%AshHq.Docs.Module{
name: name,
library_name: library_name,
version_name: version_name,
library_id: library_id
},
selected_versions
) do
"/docs/module/#{sanitize_name(library_name)}/#{version(version_name, library_id, selected_versions)}/#{sanitize_name(name)}"
end
def doc_link(
%AshHq.Docs.Function{
name: name,
arity: arity,
type: type,
module_name: module_name,
library_name: library_name,
version_name: version_name,
library_id: library_id
},
selected_versions
) do
"/docs/module/#{sanitize_name(library_name)}/#{version(version_name, IO.inspect(library_id), selected_versions)}/#{sanitize_name(module_name)}/##{type}-#{sanitize_name(name)}-#{arity}"
end
def doc_link(
%AshHq.Docs.Guide{
route: route,
library_version: %{
library_name: library_name,
version: version,
library_id: library_id
}
},
selected_versions
) do
"/docs/guides/#{sanitize_name(library_name)}/#{version(version, library_id, selected_versions)}/#{route}"
end
def doc_link(
%AshHq.Docs.LibraryVersion{
library_name: library_name,
version: version,
library_id: library_id
},
selected_versions
) do
"/docs/dsl/#{sanitize_name(library_name)}/#{version(version, library_id, selected_versions)}"
end
def doc_link(
%AshHq.Docs.Extension{
library_version: %{
library_name: library_name,
version: version,
library_id: library_id
},
name: name
},
selected_versions
) do
"/docs/dsl/#{sanitize_name(library_name)}/#{version(version, library_id, selected_versions)}/#{sanitize_name(name)}"
end
def doc_link(item, selected_versions) do
case item do
%AshHq.Docs.Dsl{} = item ->
"/docs/dsl/#{sanitize_name(library.name)}/#{sanitize_name(name)}/#{sanitize_name(extension)}/#{Enum.map_join(item.path ++ [item.name], "/", &sanitize_name/1)}"
"/docs/dsl/#{sanitize_name(item.library_name)}/#{version(item.version_name, item.library_id, selected_versions)}/#{sanitize_name(item.extension_name)}/#{Enum.map_join(item.path ++ [item.name], "/", &sanitize_name/1)}"
%AshHq.Docs.Option{} = item ->
"/docs/dsl/#{sanitize_name(library.name)}/#{sanitize_name(name)}/#{sanitize_name(extension)}/#{Enum.map_join(item.path, "/", &sanitize_name/1)}##{item.name}"
"/docs/dsl/#{sanitize_name(item.library_name)}/#{version(item.version_name, item.library_id, selected_versions)}/#{sanitize_name(item.extension_name)}/#{Enum.map_join(item.path, "/", &sanitize_name/1)}##{item.name}"
end
end
def module_link(library, version, module) do
"/docs/module/#{sanitize_name(library.name)}/#{sanitize_name(version)}/#{sanitize_name(module)}"
end
def doc_link(%AshHq.Docs.Module{
name: name,
library_name: library_name,
version_name: version_name
}) do
"/docs/module/#{sanitize_name(library_name)}/#{sanitize_name(version_name)}/#{sanitize_name(name)}"
end
def doc_link(%AshHq.Docs.Function{
name: name,
arity: arity,
type: type,
module_name: module_name,
library_name: library_name,
version_name: version_name
}) do
"/docs/module/#{sanitize_name(library_name)}/#{sanitize_name(version_name)}/#{sanitize_name(module_name)}/##{type}-#{sanitize_name(name)}-#{arity}"
end
def doc_link(%AshHq.Docs.Guide{
route: route,
library_version: %{library_name: library_name, version: version}
}) do
"/docs/guides/#{sanitize_name(library_name)}/#{sanitize_name(version)}/#{route}"
end
def doc_link(%AshHq.Docs.LibraryVersion{library_name: library_name, version: version}) do
"/docs/dsl/#{sanitize_name(library_name)}/#{sanitize_name(version)}"
end
def doc_link(%AshHq.Docs.Extension{
library_version: %{library_name: library_name, version: version},
name: name
}) do
"/docs/dsl/#{sanitize_name(library_name)}/#{sanitize_name(version)}/#{sanitize_name(name)}"
end
def doc_link(item) do
case item do
%AshHq.Docs.Dsl{} = item ->
"/docs/dsl/#{sanitize_name(item.library_name)}/#{sanitize_name(item.version_name)}/#{sanitize_name(item.extension_name)}/#{Enum.map_join(item.path ++ [item.name], "/", &sanitize_name/1)}"
%AshHq.Docs.Option{} = item ->
"/docs/dsl/#{sanitize_name(item.library_name)}/#{sanitize_name(item.version_name)}/#{sanitize_name(item.extension_name)}/#{Enum.map_join(item.path, "/", &sanitize_name/1)}##{item.name}"
defp version(version_name, library_id, selected_versions) do
if selected_versions[library_id] == "latest" do
"latest"
else
version_name
end
end

View file

@ -7,12 +7,12 @@ defmodule AshHqWeb.AppViewLive do
alias Phoenix.LiveView.JS
require Ash.Query
data(configured_theme, :string, default: :system)
data(searching, :boolean, default: false)
data(selected_versions, :map, default: %{})
data(libraries, :list, default: [])
data(selected_types, :map, default: %{})
data(sidebar_state, :map, default: %{})
data configured_theme, :string, default: :system
data searching, :boolean, default: false
data selected_versions, :map, default: %{}
data libraries, :list, default: []
data selected_types, :map, default: %{}
data sidebar_state, :map, default: %{}
def render(assigns) do
~F"""
@ -58,7 +58,7 @@ defmodule AshHqWeb.AppViewLive do
href="/docs/guides/ash/main/getting-started"
target="_blank"
class="dark:text-gray-400 dark:hover:text-gray-200 hover:text-gray-600"
>Get Started</a>
>Quick Start</a>
<div>|</div>
<a
href="/docs/guides/ash/main/overview"
@ -212,25 +212,40 @@ defmodule AshHqWeb.AppViewLive do
socket.assigns.libraries
|> Enum.map(fn library ->
Map.update!(library, :versions, fn versions ->
latest_version =
Enum.find(versions, &String.contains?(&1.version, ".")) || Enum.at(versions, 0)
Enum.map(versions, fn version ->
if version.id == socket.assigns[:selected_versions][library.id] do
dsls_query = Ash.Query.sort(AshHq.Docs.Dsl, order: :asc)
options_query = Ash.Query.sort(AshHq.Docs.Option, order: :asc)
functions_query = Ash.Query.sort(AshHq.Docs.Function, name: :asc, arity: :asc)
if (socket.assigns[:selected_versions][library.id] == "latest" && latest_version &&
version.id == latest_version.id) ||
version.id == socket.assigns[:selected_versions][library.id] do
dsls_query = AshHq.Docs.Dsl |> Ash.Query.sort(order: :asc) |> load_for_search()
options_query =
AshHq.Docs.Option |> Ash.Query.sort(order: :asc) |> load_for_search()
functions_query =
AshHq.Docs.Function
|> Ash.Query.sort(name: :asc, arity: :asc)
|> load_for_search()
guides_query = AshHq.Docs.Guide |> Ash.Query.new() |> load_for_search()
modules_query =
AshHq.Docs.Module
|> Ash.Query.sort(order: :asc)
|> Ash.Query.load(functions: functions_query)
|> load_for_search()
extensions_query =
AshHq.Docs.Extension
|> Ash.Query.sort(order: :asc)
|> Ash.Query.load(options: options_query, dsls: dsls_query)
|> load_for_search()
AshHq.Docs.load!(version,
extensions: extensions_query,
guides: [],
guides: guides_query,
modules: modules_query
)
else
@ -327,7 +342,7 @@ defmodule AshHqWeb.AppViewLive do
:selected_types,
selected_types
)
|> push_event("selected_versions", selected_versions)
|> push_event("selected-versions", selected_versions)
|> push_event("selected_types", %{types: selected_types})
end
)
@ -364,4 +379,13 @@ defmodule AshHqWeb.AppViewLive do
)
|> JS.dispatch("js:focus", to: "#search-input")
end
defp load_for_search(query) do
Ash.Query.load(
query,
IO.inspect(AshHq.Docs.Extensions.Search.load_for_search(query.resource),
label: inspect(query.resource)
)
)
end
end

View file

@ -1,5 +1,5 @@
%{
"ash": {:git, "https://github.com/ash-project/ash.git", "a012287c3b621ac15d0d7b00735c6ebc9c477163", []},
"ash": {:git, "https://github.com/ash-project/ash.git", "ea1adcf2309e6a317cda288af949da2ae582f7eb", []},
"ash_phoenix": {:git, "https://github.com/ash-project/ash_phoenix.git", "9de0ff87780bd16d06dcde1bac6a8e041a31659d", []},
"ash_postgres": {:git, "https://github.com/ash-project/ash_postgres.git", "48c6142040d389272d235b7e62d354a3a37a825e", []},
"castore": {:hex, :castore, "0.1.16", "2675f717adc700475345c5512c381ef9273eb5df26bdd3f8c13e2636cf4cc175", [:mix], [], "hexpm", "28ed2c43d83b5c25d35c51bc0abf229ac51359c170cba76171a462ced2e4b651"},

View file

@ -0,0 +1,29 @@
defmodule AshHq.Repo.Migrations.MigrateResources5 do
@moduledoc """
Updates resources based on their most recent snapshots.
This file was autogenerated with `mix ash_postgres.generate_migrations`
"""
use Ecto.Migration
def up do
alter table(:libraries) do
add :description, :text
end
alter table(:guides) do
modify :category, :text, null: false
end
end
def down do
alter table(:guides) do
modify :category, :text, null: true
end
alter table(:libraries) do
remove :description
end
end
end

View file

@ -0,0 +1,110 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "fragment(\"uuid_generate_v4()\")",
"generated?": false,
"primary_key?": true,
"references": null,
"size": null,
"source": "id",
"type": "uuid"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "order",
"type": "bigint"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "name",
"type": "text"
},
{
"allow_nil?": false,
"default": "\"\"",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "text",
"type": "text"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "text_html",
"type": "text"
},
{
"allow_nil?": false,
"default": "\"Guides\"",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "category",
"type": "text"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "route",
"type": "text"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": {
"destination_field": "id",
"destination_field_default": null,
"destination_field_generated": null,
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"name": "guides_library_version_id_fkey",
"on_delete": "delete",
"on_update": null,
"table": "library_versions"
},
"size": null,
"source": "library_version_id",
"type": "uuid"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"has_create_action": true,
"hash": "E01C44270CD9127EF1BDE286D0DA9AED8BB0A8CE72BE9FF3F76C82148C0A413F",
"identities": [],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.AshHq.Repo",
"table": "guides"
}

View file

@ -0,0 +1,70 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "fragment(\"uuid_generate_v4()\")",
"generated?": false,
"primary_key?": true,
"references": null,
"size": null,
"source": "id",
"type": "uuid"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "name",
"type": "text"
},
{
"allow_nil?": false,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "display_name",
"type": "text"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "description",
"type": "text"
},
{
"allow_nil?": true,
"default": "[]",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "track_branches",
"type": [
"array",
"text"
]
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"has_create_action": true,
"hash": "70099624C119120DD5B91858CB2497E6D76C81E6CFFD60966D85F5A3CE534D04",
"identities": [],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.AshHq.Repo",
"table": "libraries"
}