Summary
compileSchema promises to validate all supplied schemas and its main compilation path accumulates diagnostics, but addRemoteSchema replaces the owner's schemaErrors/schemaAnnotations on every call and omits schemaId assignment. A remotes list therefore retains only the last remote's diagnostics.
Impact: With two invalid entries in options.remotes, schemaErrors has length 1, loses the first remote's #/anyOf/0 error, and leaves the surviving #/type error's schemaId undefined.
Code path
Checked against current main at commit 1cc2a3045f615ef67104f02449c6fd58afebeb6a.
README.md:118-124
src/compileSchema.ts:176-195
src/SchemaNode.ts:580-594
Steps to reproduce
Validation level: current HEAD supplemental dynamic witness reproduced.
node -e "const fs=require('fs'),vm=require('vm'); vm.runInThisContext(fs.readFileSync('dist/jlib.js','utf8')); const remotes=[{'\$id':'https://a.example/schema',anyOf:[999]},{'\$id':'https://b.example/schema',type:'invalid-type'}]; const node=jlib.compileSchema({}, {remotes}); console.log(JSON.stringify({count:node.schemaErrors.length,schemaIds:node.schemaErrors.map(e=>e.data.schemaId),pointers:node.schemaErrors.map(e=>e.data.pointer)}))"
Observed output:
{"count":1,"schemaIds":[null],"pointers":["#/type"]}
Neighboring control:
node -e "const fs=require('fs'),vm=require('vm'); vm.runInThisContext(fs.readFileSync('dist/jlib.js','utf8')); const remotes=[{'\$id':'https://a.example/schema',anyOf:[999]}]; const node=jlib.compileSchema({}, {remotes}); console.log(JSON.stringify({count:node.schemaErrors.length,schemaIds:node.schemaErrors.map(e=>e.data.schemaId),pointers:node.schemaErrors.map(e=>e.data.pointer)}))"
Control output:
{"count":1,"schemaIds":["https://a.example/schema"],"pointers":["#/anyOf/0"]}
Expected behavior
The documented/source-grounded contract should hold without the drift described above.
Actual behavior
Registering later remotes erases earlier diagnostics, loses provenance, and can bypass throwOnInvalidSchema.
Existing coverage
I checked the current issue and PR lists for overlapping titles/root-cause keywords before filing this. I did not find an item covering this same root cause.
Suggested fix
Accumulate addRemoteSchema diagnostics and assign each remote's schemaId instead of replacing the existing arrays.
Suggested tests
- Add a regression test for the reproduction above.
- Add a neighboring control assertion so the intended non-bug path remains covered.
Submitted with Codex.
Summary
compileSchema promises to validate all supplied schemas and its main compilation path accumulates diagnostics, but addRemoteSchema replaces the owner's schemaErrors/schemaAnnotations on every call and omits schemaId assignment. A remotes list therefore retains only the last remote's diagnostics.
Impact: With two invalid entries in options.remotes, schemaErrors has length 1, loses the first remote's #/anyOf/0 error, and leaves the surviving #/type error's schemaId undefined.
Code path
Checked against current
mainat commit1cc2a3045f615ef67104f02449c6fd58afebeb6a.README.md:118-124src/compileSchema.ts:176-195src/SchemaNode.ts:580-594Steps to reproduce
Validation level: current HEAD supplemental dynamic witness reproduced.
node -e "const fs=require('fs'),vm=require('vm'); vm.runInThisContext(fs.readFileSync('dist/jlib.js','utf8')); const remotes=[{'\$id':'https://a.example/schema',anyOf:[999]},{'\$id':'https://b.example/schema',type:'invalid-type'}]; const node=jlib.compileSchema({}, {remotes}); console.log(JSON.stringify({count:node.schemaErrors.length,schemaIds:node.schemaErrors.map(e=>e.data.schemaId),pointers:node.schemaErrors.map(e=>e.data.pointer)}))"Observed output:
Neighboring control:
node -e "const fs=require('fs'),vm=require('vm'); vm.runInThisContext(fs.readFileSync('dist/jlib.js','utf8')); const remotes=[{'\$id':'https://a.example/schema',anyOf:[999]}]; const node=jlib.compileSchema({}, {remotes}); console.log(JSON.stringify({count:node.schemaErrors.length,schemaIds:node.schemaErrors.map(e=>e.data.schemaId),pointers:node.schemaErrors.map(e=>e.data.pointer)}))"Control output:
Expected behavior
The documented/source-grounded contract should hold without the drift described above.
Actual behavior
Registering later remotes erases earlier diagnostics, loses provenance, and can bypass throwOnInvalidSchema.
Existing coverage
I checked the current issue and PR lists for overlapping titles/root-cause keywords before filing this. I did not find an item covering this same root cause.
Suggested fix
Accumulate addRemoteSchema diagnostics and assign each remote's schemaId instead of replacing the existing arrays.
Suggested tests
Submitted with Codex.