# `Statifier.Validator.Checks.Enums`
[🔗](https://github.com/riddler/statifier-ex/blob/v2.0.0/lib/statifier/validator/checks/enums.ex#L1)

Out-of-range enumerated attribute values (spec 3.2.1 and 3.5).

`Statifier.Lowering.Attributes.atom/4` maps an unrecognised value onto the
attribute's default rather than erroring, so `type="sideways"` lowers to
`:external` and `binding="whenever"` lowers to `:early` - indistinguishable
on the struct from the valid spellings that produce the same atom. Only the
source text can tell them apart, which is what `validate/2`'s `source`
argument exists for: the written attribute's span is sliced
back out and compared against the range.

Four reasons, one per enumerated attribute this check owns:

- `{:transition_bad_type, raw}` - a `<transition type="...">` that is
  neither `"internal"` nor `"external"` (spec 3.5). Covers every
  transition the document holds, including those under an `<initial>`
  element and a `<history>`'s default transition, since
  `Statifier.Validator.Context` already walks all three.
- `{:scxml_bad_binding, raw}` - an `<scxml binding="...">` that is neither
  `"early"` nor `"late"` (spec 3.2.1).
- `{:scxml_bad_datamodel, raw}` - an `<scxml datamodel="...">` outside
  `["predicator", "elixir", "null", "ecmascript", "xpath"]` (spec 3.2.1).
  3.2.1's Valid Values clause is itself open-ended - `"null", "ecmascript",
  "xpath" or other platform-defined values` - so the allow-list is the
  spec's three named values plus this platform's two
  (`docs/datamodel.md:6-7`), not a strict `["predicator", "elixir"]`
  rejection of everything else. Accepting `"ecmascript"` does **not** mean
  this engine implements ECMAScript: which datamodel actually runs is
  ADR-0004's answer and the corpus tooling's, never this attribute's. What
  this check catches is a typo (`datamodel="predicater"`,
  `datamodel="javascript"`) - the ordinary job of a validator check on an
  enumerated NMTOKEN. An absent `datamodel` attribute is `"predicator"`,
  this platform's documented default (3.2.2), so it is never reported.
- `{:invoke_bad_autoforward, raw}` - an `<invoke autoforward="...">` that
  is neither `"true"` nor `"false"` (spec 6.4.1). Walks every `<invoke>`
  in the document directly (this check's own `flatten/1`, the same shape
  `Checks.Donedata`/`Checks.Content`/`Checks.Param` use), since
  `Statifier.Validator.Context` indexes transitions but not invocations.

The fifth enumerated attribute lowering maps this way, `<history
type="...">`, is **not** here: `Statifier.Validator.Checks.History` reports
it as `:history_bad_type` under spec 3.10, alongside that element's other
structural rules. The slice-and-compare shape is duplicated between the two
modules rather than shared, so each check stays readable on its own and
neither has to reach into the other's spec section.

An attribute that was never written has no `attribute_locations` entry and
is not reported - an absent enumerated attribute is its default, which is
always in range.

# `check`

```elixir
@spec check(
  document :: Statifier.Document.t(),
  context :: Statifier.Validator.Context.t()
) :: [
  Statifier.Validator.Error.t()
]
```

Returns a `:transition_bad_type` error for every `<transition type="...">`
whose written value is neither `"internal"` nor `"external"`, a
`:scxml_bad_binding` error when the root `<scxml binding="...">` is neither
`"early"` nor `"late"`, a `:scxml_bad_datamodel` error when the root
`<scxml datamodel="...">` is outside `["predicator", "elixir", "null",
"ecmascript", "xpath"]`, and an `:invoke_bad_autoforward` error for every
`<invoke autoforward="...">` whose written value is neither `"true"` nor
`"false"`. All four slice the raw source text rather than trusting the
lowered atom/boolean, since an out-of-range value and a valid one can
lower to the same default. Returns `[]` when all four enumerated
attributes are in range everywhere they were written.

---

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