# `Macaw.Layers.Bundle`
[🔗](https://github.com/dcoai/macaw_layers/blob/v0.3.0/lib/macaw/layers/bundle.ex#L1)

A portable set of documents (spec/03 §3): the transitive closure of what a page needs,
with where it came from, in a map form and a JSON form so it can leave the node it was
made on, and imported into another store as a relocation (`Macaw.Layers.Store.relocate/3`).

    %Macaw.Layers.Bundle{
      provenance: %{store: "metresis", exported_at: ~U[2026-10-20 09:30:00.000000Z], version: 1},
      entries: [%{id: {:style, 11}, name: "Styles:thick", fragment: %{style: %{width: 3}}}],
      snapshot: nil
    }

A bundle is a term, judged as one by `check/2` however it was made: this module is a
`Macaw.Layers.Document`, whose `validate/1` judges the envelope, and `check/2` adds each
entry's kind against the store's types. Its map and JSON forms are made at the edge:
`to_map/2` and `to_json/2` write them, and `from_map/3` and `from_json/3` read one
through the JSON adapter's gates (`Macaw.Layers.Form.gates/3`, spec/01 §5) with the
bundle's own version line (this module is a `Macaw.Layers.Version`), decode the envelope,
read each entry's fragment by its own type, at the type's own version where the type has
a line, and then check the term.

Ported from visualize's `Visualize.Chart.Builder.Bundle` and its codec, without boards.

# `provenance`

```elixir
@type provenance() :: %{
  store: String.t() | atom(),
  exported_at: DateTime.t(),
  version: pos_integer()
}
```

Where a bundle came from, when, and the bundle version it was exported at.

# `t`

```elixir
@type t() :: %Macaw.Layers.Bundle{
  entries: [Macaw.Layers.Store.entry()],
  provenance: provenance(),
  snapshot: map() | nil
}
```

# `check`

```elixir
@spec check(t(), [module()]) :: {:ok, t()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

The bundle judged as a term (§3.2), however it was made: its envelope by `validate/1`
(`Macaw.Layers.Document.check/2`), and each entry's kind against the type its id names,
`types` given as a store's: an id of a kind none of them has is `{:type, :id}`, a
fragment that derives to no kind `:no_kind`, and one that derives to another kind than
its id's `{:kind, id_kind, derived}`. `{:ok, bundle}`, or every fault, ordered by path.

This is how a bundle built in Elixir is judged; no form is written or read, so a value
only an encoding can't hold fails at that encoding (`to_map/2`), not here.

# `export`

```elixir
@spec export(
  Macaw.Layers.Store.assign(),
  Macaw.Layers.Store.id() | [Macaw.Layers.Store.id()],
  String.t() | atom()
) :: t()
```

The closure of `ids` through a store (§3.1): each entry, and every entry it refers to
(`Macaw.Layers.Fragment.refs/2`, use sites included), transitively, each once, in
dependency order. An id the store no longer holds is skipped. `source` is the host's
name for the store, written in the provenance.

# `from_json`

```elixir
@spec from_json(String.t(), [module()], keyword()) ::
  {:ok, t()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

The bundle a JSON document holds, as `from_map/3` reads it, every gate first.

# `from_map`

```elixir
@spec from_map(term(), [module()], keyword()) ::
  {:ok, t()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

The bundle a map form holds (§3.2): through the JSON adapter's gates with this module's
version line (`Macaw.Layers.Form.gates/3`: the limits, the stamp, the migration, the
recount), then the envelope's own decode of the map form, then each entry's fragment read
by the type its id names, at the type's own version, and then the bundle judged as a term
(`check/2`): `{:ok, bundle}`, or the faults of the first step that refuses it, by path.
`types` are a store's; `limits:` as `Macaw.Layers.Form.gates/3` takes them.

# `to_json`

```elixir
@spec to_json(t(), [module()]) ::
  {:ok, String.t()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

The bundle's JSON form.

# `to_map`

```elixir
@spec to_map(t(), [module()]) ::
  {:ok, map()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

The bundle's map form (§3.2), each entry's fragment in its type's map form, stamped with
the type's version where the type has a line: `{:ok, map}`, or every value without a
form, by path.

---

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