Repository navigation
Reject a $ref in place of a schema that leads to an array (an enum, a required list, an array document) instead of emitting a tuple - #886
Draft
claude[bot] wants to merge 1 commit into
Conversation
…of emitting a tuple
A `$ref` where a schema is expected -- a property, an `items`, an `allOf` member, the
root -- that led to an array (a pointer onto an `enum` or `required` list, in this
document or another, or a file whose whole content is an array) came out as a tuple of
the list's members (`y?: ["metres", "feet"]`), or as `unknown` at the root, with no
error: the non-schema guard took any object for a schema, arrays included. It now gets
the guard's error, naming the array ("an array of 2 items") and the file, if any.
Still fine: a `$ref` onto a list in a data position (`enum`, `examples`, `default`,
vendor keys), a list of property names under `dependencies`, and a `$ref` standing in
for the list of schemas an `items` or `extends` may hold.
The root's `$ref` is looked up before dereferencing, since the ref-parser reports it to
no hook and, with a keyword beside it, merges an array's members into the root as keys
`0`, `1`, ...; the in-document path now reports where each replacement sits, as the
ref-parser does, and judges what it collected.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Output change: a schema with a
$refin a schema position that leads to a list (anenum, arequiredlist, a file that is an array) now fails with an error where it compiled to a tuple of the list's members or tounknown; every other schema's output is byte-identical (corpus, conformance and snapshots unchanged).A
$refthat stands where a schema is expected has to lead to an object or a boolean. #877 made a$refonto a string, a number,nullor an empty file an error that names the target. An array slipped through that guard, because it took any object for a schema, and came out as a made-up type.Before (master):
After:
for
{"title": "A", "type": "object", "definitions": {"unit": {"type": "string", "enum": ["metres", "feet"]}}, "properties": {"y": {"$ref": "#/definitions/unit/enum"}}}The same error, naming the file, for a pointer into another file (
$ref "lists.json#/definitions/u/required" at properties/y resolves to an array of 1 item in /abs/lists.json, which is not a JSON Schema.) and for a file whose whole content is an array ($ref "arr.json" at properties/y resolves to /abs/arr.json, which is not a JSON Schema: it parses to an array of 2 items.), through a file that is itself a$refto such a file, and at the root ({"$ref": "arr.json"},{"title": "T", "$ref": "arr.json"},{"$ref": "#/definitions/unit/enum", "definitions": …}), where the whole schema used to compile tounknown.Decided here: a whole-document array
$ref({"$ref": "arr.json"}) is an error too. An array is not a JSON Schema in any draft, and the alternative wasexport type T = unknownwith no word about why.Still fine, and pinned by tests:
$refonto a list in a data position:enum: {"$ref": "#/definitions/unit/enum"},examples,default,tsEnumNames, vendor keys;dependencies(dependencies: {"b": {"$ref": "#/required"}});$refstanding in for the list of schemas anitems(orextends) may hold (items: {"$ref": "#/definitions/pair/items"}still gives[] | [string] | [string, number, ...unknown[]]). A list that holds anything but objects and booleans is rejected there (items: {"$ref": "arr.json"}with[1, 2]).Why
isSchemain the resolver accepted any non-null object. The ref-parser'sonDereferencehook therefore never collected an array as a suspect, the in-document fast path never reported where its replacements sat (it had no non-schema target to report), and the root's own$refis reported to no hook at all: with a keyword beside it (atitle, or thedefinitionsa pointer into the same document leads through) the ref-parser merges the array's members into the root as keys0,1, …, which then reads as an object.How
isSchemaexcludes arrays;describeNonSchemaTargetnames one ("an array of 2 items").onDereferencecollects every non-schema target, arrays included; the in-document path passes the holder and key to it, as the ref-parser does, and judges what it collected.$refis looked up through the ref-parser's$refs.getright after every document is read and before dereferencing, and rejected there if it leads to an array (continueOnErroris switched off for that one lookup, so a pointer the ref-parser cannot resolve is neither thrown nor recorded there; its own error follows as before).misplacedTargets: an array where a single schema stands is misplaced; standing in for a wholeitems/extendsvalue it is fine when every member is an object or boolean; an entry ofdependenciesmay be a list of names.refToNonSchemaValue.ts(in-document pointer, pointer into another file, array document, both at a property and at the root) fail on master 5caacfc and pass here;test/refTargets.test.tspins the three shapes that keep compiling and thecontinueOnErrorlookup. New fixturetest/resources/NotASchema/array.json.Tests
CI=true bun run test: 688 pass, 0 fail, 351 snapshots (master 5caacfc: 680 / 0 / 351). No.snapchange.node test/corpus/run.js --e2e --out …) on master and this head: 78 outputs byte-identical, 0 diagnostics.bun run conformance: all 2404 groups match the baseline.$refs onto non-schema values in every schema position (checks/ref-target-probe/run.jsas it was before this change), master vs this head: identical on 121 rows, the four that change are the array rows, eachOK→ theReferenceErrorabove:pos.wholeItems.arrDoc(items: {$ref: 'arr.json'}with[1,2]),root.arr({$ref: 'arr.json'}),ptr.inDoc.required(properties/y→#/required),ok.arrDoc.props(properties/y→arr.json).pos.dependencies.listDocdoes not change and must not (dependencies: {a: ['b', 'c']}is a valid schema). The 79-row root-pointer probe: 2 rows change,root.array.enumandroot.array.required(unknown→ the error). The 25-row two-route probe: no row changes, and no document changes route.dependencies, data positions, the root with$id, chains and siblings): 24 change, allOK→ the error; 12 keep compiling as they must, and the 13th (a$refin place of a wholeallOflist) crashes on both trees, per Known limits below. Four rows added to the root-pointer probe:compileFromFile, acompileFilesmember and the CLI on an array root now exit 1 with the error; a root pointer into a file whose own root$refleads onto a list is still the ref-parser's missing-pointer error on both trees, the twin of Report a root$refonto a keyword value as not a JSON Schema, not as a missing pointer #883's cross-file case.Known limits
$refonto a list with a type-shaping sibling ({"type": "string", "$ref": "#/definitions/unit/enum"}) is merged by the ref-parser into an object before any hook sees the list, on both dereferencing routes alike, and compiles tostring(master: a tuple). Judging it would mean reading the target before the merge on the ref-parser's route, which has no hook for that; left as is, and noted in the resolver.$refstanding in for a wholeallOf/anyOf/oneOflist crashes withTypeError: arr.forEach is not a functionin the prenormalizer's walk, before dereferencing, on master and here alike; not this change's.$refwith an annotation sibling is theallOf/0the prenormalizer rewrites it to, as for every non-schema target since Report a$refwhose target is not a schema (empty file,null/string/number document, plain-text YAML) instead of crashing #877.Independent of #883 (root pointer onto a string):
git merge-treeof the two heads is clean, and once both are in, an in-document root pointer onto a list is caught by #883's guard first, with the same message.Changelog
$refin place of a schema that leads to a list (anenum, arequiredlist, an array document) is now an error naming the list, instead of a tuple of its members orunknown.