Issue #3620153: Add the card shape, and the engine it renders through
Half of what the forks of this module call structure builders is one thing: render a referenced entity as a small card by that bundle's own description. Written by hand there are dozens of them and they drift, so the same bundle looks different in two places for no reason anybody remembers.
Doing that needs an engine, and there was none: a card renders its target through the same thing that renders a top-level response, so that one description of a bundle serves both the page about it and every card of it elsewhere. The engine is therefore what most of this change is, and it is built for the callers still to come rather than for this one shape.
What it guarantees:
- It does not fail a request. A property naming a shape that is not installed, a field the entity no longer has, or a shape that throws costs that one key and a line in the log. A model is written against a site that changes underneath it, and a response missing one key is repairable by whoever notices it; a 500 on a page that is otherwise fine is not.
- It does not answer with a plausible wrong value. A shape is never handed a field type it never claimed, because a field whose type changed under a structure would otherwise come back as something that looks right.
- The order of the keys is the order of the properties, so two recorded responses of an unchanged site are identical.
- Nesting is bounded by two separate rules. An entity is never rendered inside itself, which is exact and loses nothing, since what would be rendered is already in the response one level up. And a depth bound stops the model nobody thought of; it is a setting, because how deeply a model nests differs between sites.
- Cache metadata is collected through the whole nesting, and the context of a render is restored as it unwinds, a property that threw included.
Access is the card's own doing: it asks each target whether this reader may see it. A card is a second door into an entity, and a door that does not ask is how an unpublished article reaches a front end from a page that is published.
Where structures are kept is not decided here. A card may carry one, which is what the existing resources will use when they move onto the library, or name one that a repository resolves - the interface for that is defined, and the implementation arrives with the configuration entity.
Closes #3620153