moonschema JSON Schema · MoonBit

Showcase

A fault is
two places at once.

One end is where the instance was, the other is the keyword that refused it. Everything on this page is drawn from real runs: the faults are computed by the compiled package as the page loads, and the threads are where those faults point.

The map

One thread per reason

Pick a case. Hover a reason to light its thread; the two nodes it joins are the two places the message is talking about.

engine

schema

instance

    a fault, from the schema keyword to the instance node hover a reason above to pick one out

    The hard keyword

    What was never evaluated

    unevaluatedProperties is the one keyword that cannot be answered from the schema alone. It asks what every other keyword — and every in-place applicator it reached — has already looked at. A branch of allOf that validated hands its members over; a branch that failed hands over nothing.

    THE SCHEMA allOf branch 0 properties: { id } branch 1 properties: { total } unevaluatedProperties false id evaluated total evaluated evaluated by nobody "id": "ORD-12" "total": 0 "note": "gift" THE INSTANCE
    Two branches, two members evaluated. The third member was evaluated by nobody, so the keyword at the bottom is the only one left to refuse it.

    the run this diagram is drawn from

    /email: no instance validates against false (schema at /unevaluatedProperties)

    The keyword is schema and the message is about false, because the schema in question is the boolean false — a schema that refuses everything. Written as "unevaluatedProperties": { "type": "string" } the same position would say not of type string instead.

    note
    The order matters. Everything that can evaluate something runs first, and the unevaluated* keywords run last, on what is left. That is why they cannot be written before the applicators they ask about.

    Addressing

    Where a reference points

    A $ref is resolved against the base URI in effect — RFC 3986, the same rules a browser follows for a relative link. Which means "money" does not mean a key called money; it means a document beside this one.

    THE REFERENCE THE BASE IN FORCE WHAT IT RESOLVES TO "money" https://example.test/order against merge https://example.test/money a relative name is merged onto the directory of the base "#/$defs/port" https://example.test/order keeps as is the pointer, in this document the base is kept, and nothing outside this document is looked for "int" https://example.test/a/b against append https://example.test/a/int the last segment of the base is replaced by the name
    Relative names resolve like links. A fragment beginning with / is a JSON Pointer into a document, and a document is only reachable when the caller has handed it over.