# `JSONSchex.Ref`
[🔗](https://github.com/xinz/jsonschex/blob/main/lib/jsonschex/ref.ex#L1)

Generic selected `$ref` resolver for JSON-like documents.

`resolve_selected/2` walks maps and lists and resolves only `$ref` nodes that
the caller selects. This keeps domain-specific knowledge (for example,
OpenAPI Reference Object locations) outside JSONSchex while reusing the same
low-level reference mechanics as JSON Schema: URI resolution, JSON Pointer
lookup, external loading, base URI propagation, and cycle detection.

# `loader`

```elixir
@type loader() :: (String.t() -&gt; loader_result())
```

# `loader_result`

```elixir
@type loader_result() ::
  {:ok, map() | boolean()}
  | {:ok, %{:document =&gt; map() | boolean(), optional(:base_uri) =&gt; String.t()}}
  | {:error, term()}
```

# `selector`

```elixir
@type selector() :: (list(), map() -&gt; boolean())
```

# `resolve_selected`

```elixir
@spec resolve_selected(
  term(),
  keyword()
) :: {:ok, term()} | {:error, JSONSchex.Ref.Error.t()}
```

Resolves selected `$ref` nodes in a JSON-like document.

## Options

- `:select` — required `(path, node -> boolean())` callback. `path` points to
  the map containing `$ref`, not to the `$ref` key.
- `:base_uri` — optional starting base URI/path for resolving relative
  external references.
- `:loader` — optional loader for external resources.

Selected `$ref` nodes are replaced by the resolved target value. Unselected
`$ref` nodes are preserved and are not interpreted as references, while their
sibling values continue to be walked. Consequently, `:select` is invoked for
descendant `$ref` nodes beneath an unselected ref map that earlier releases
skipped. Selectors with side effects should account for these additional calls.
When an external selected target is
inlined, unselected `$ref` string values anywhere beneath that target are rebased
against the loaded resource's effective base URI, including nested `$id`
boundaries, so they continue to point at their original resource.

## Examples

Resolve only the selected local `$ref` node:

    iex> document = %{
    ...>   "parameter" => %{"$ref" => "#/components/parameters/UserId"},
    ...>   "schema" => %{"$ref" => "#/components/schemas/User"},
    ...>   "components" => %{
    ...>     "parameters" => %{"UserId" => %{"name" => "id", "in" => "path"}},
    ...>     "schemas" => %{"User" => %{"type" => "object"}}
    ...>   }
    ...> }
    iex> select = fn
    ...>   ["parameter"], %{"$ref" => _} -> true
    ...>   _path, _node -> false
    ...> end
    iex> {:ok, resolved} = JSONSchex.Ref.resolve_selected(document, select: select)
    iex> resolved["parameter"]
    %{"in" => "path", "name" => "id"}
    iex> resolved["schema"]
    %{"$ref" => "#/components/schemas/User"}

Resolve a selected external `$ref` with a loader:

    iex> document = %{"parameter" => %{"$ref" => "./common.yaml#/components/parameters/UserId"}}
    iex> loader = fn "/api/common.yaml" ->
    ...>   {:ok, %{"components" => %{"parameters" => %{"UserId" => %{"name" => "id", "in" => "path"}}}}}
    ...> end
    iex> {:ok, resolved} = JSONSchex.Ref.resolve_selected(document,
    ...>   base_uri: "/api/openapi.yaml",
    ...>   loader: loader,
    ...>   select: fn _path, %{"$ref" => _} -> true; _path, _node -> false end
    ...> )
    iex> resolved["parameter"]
    %{"in" => "path", "name" => "id"}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
