# `Oasis.Spec.Document`
[🔗](https://github.com/xinz/oasis/blob/main/lib/oasis/spec/document.ex#L1)

Represents a loaded and prepared OpenAPI document.

This struct is the successful return value of `Oasis.Spec.read/1`. Consumers
should pass the complete value through Oasis generation so its source and
reference context remain available; `:schema` may be inspected as the
normalized generation view.

`Oasis.Spec.Document` is responsible only for loading and tracking source
metadata for YAML/JSON OpenAPI documents. It intentionally does not interpret
OpenAPI structures or JSON Schema semantics.

The `:source_path` field is important because it becomes the base URI/path used
by:

- `Oasis.Spec.OpenAPIRefResolver` when resolving external OpenAPI Reference Objects
- `JSONSchex.bundle_fragment/2` when schema refs need relative file resolution

External loading returns JSONSchex-compatible loader metadata with `:base_uri`
so loaded files can resolve their own relative refs correctly.

# `load_external_error`

```elixir
@type load_external_error() ::
  {load_external_error_status(), String.t()}
  | {load_external_error_status(), String.t(), String.t()}
  | {load_external_error_status(), String.t(), term()}
```

Structured error returned by `load_external/1`.

The first tuple element is a fixed string status code rather than an atom.
The remaining elements carry the offending file path plus any extra detail so
callers (most importantly the JSONSchex loader pipeline and
`Oasis.Spec.OpenAPIRefResolver`) can build precise diagnostics.

# `load_external_error_status`

```elixir
@type load_external_error_status() :: String.t()
```

String status code used in `load_external_error()`.

Allowed values are:

- `"missing_file"`
- `"yaml_parse_error"`
- `"json_parse_error"`
- `"unsupported_format"`
- `"invalid_document"`

Elixir typespecs cannot enumerate string literals directly, so this remains
`String.t()` at the type level and the fixed set is documented here.

# `pointer_path`

```elixir
@type pointer_path() :: [String.t() | non_neg_integer()]
```

# `t`

```elixir
@type t() :: %Oasis.Spec.Document{
  allow_normalized_parameters?: boolean(),
  format: String.t() | nil,
  normalized?: boolean(),
  reference_schema: map() | nil,
  schema: map(),
  schema_sources: %{optional(term()) =&gt; pointer_path()},
  source_path: String.t() | nil,
  url_aliases: %{optional(String.t()) =&gt; String.t()}
}
```

# `load`

```elixir
@spec load(String.t()) :: {:ok, {map(), keyword()}} | {:error, Exception.t()}
```

Loads a root OpenAPI document from a YAML/YML or JSON file.

On success this returns the decoded document with options suitable for
`new/2`. On failure it returns Oasis user-facing file/spec exceptions.

# `load_external`

```elixir
@spec load_external(String.t()) ::
  {:ok, %{document: map() | boolean(), base_uri: String.t()}}
  | {:error, load_external_error()}
```

Loader callback for external OpenAPI and JSON Schema resources.

## Contract

JSONSchex accepts either `{:ok, document}` or an atom-keyed metadata wrapper
`{:ok, %{document: document, base_uri: base_uri}}`. This default Oasis loader
always returns the wrapper form:

    load_external(path :: String.t()) ::
        {:ok, %{document: map() | boolean(), base_uri: String.t()}}
      | {:error, load_external_error()}

- **`:document`** — the decoded YAML/JSON document. OpenAPI resources are
  expected to be maps; JSON Schema resources may also be boolean schemas.
- **`:base_uri`** — the resolved file path or `file:` URI. Used by JSONSchex
  (and by `Oasis.Spec.OpenAPIRefResolver`) to resolve relative refs that appear
  **inside** the loaded document against the right base.

## Where it is used

- Default `:loader` for generation-time `JSONSchex.bundle_fragment/2` calls.
- Default `:loader` for `Oasis.Spec.OpenAPIRefResolver.resolve/2` when
  following external OpenAPI Reference Objects (e.g.
  `$ref: "./common.yaml#/components/parameters/UserId"`).

Callers wanting in-memory or test-only loaders may override `:loader` with
either valid JSONSchex success form, or pass `loader: nil` to opt out of
external loading entirely (any unresolved external `$ref` then raises).

# `new`

```elixir
@spec new(
  map(),
  keyword()
) :: t()
```

Wraps a decoded OpenAPI map with source metadata.

Options:

- `:source_path` - file path the document was loaded from (used as base URI).
- `:format` - `"yaml"`, `"yml"`, or `"json"`.
- `:url_aliases` - map of post-processed (Plug-style) URL key to the original
  OpenAPI URL key. Populated during path normalization and preserved here so
  downstream code can report locations using the user's original spec syntax.
- `:reference_schema` - structurally resolved but otherwise unnormalized
  OpenAPI document used as JSONSchex's reference root.
- `:schema_sources` - sidecar map from normalized generation inputs to their
  source JSON Pointer token paths in `:reference_schema`.
- `:normalized?` - internal marker set during path preparation so repeated
  preparation is idempotent without inferring state from OpenAPI field shapes.
- `:allow_normalized_parameters?` - compatibility flag for the legacy
  pre-normalized map form accepted by internal generation callers.

---

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