Skip to content

Honor draft 2020-12 $ref sibling keywords - #177

Open
samlown wants to merge 5 commits into
jsonnext:mainfrom
invopop:upstream-pr/ref-sibling-keywords
Open

samlown wants to merge 5 commits into
jsonnext:mainfrom
invopop:upstream-pr/ref-sibling-keywords

Conversation

@samlown

@samlown samlown commented Oct 27, 2026 •

Copy link
Copy Markdown

Builds on #176. This branch contains #176's commits, so only the last two commits (ecd1338, b941cb5) are new here. Once #176 is merged, this diff will shrink to those commits.

Problem

Since draft 2019-09, $ref combines with the keywords next to it. json-schema-library resolves a $ref by replacing the node with its target, which drops those keywords even when the schema declares draft 2020-12. A schema like

{ "$ref": "#/$defs/key", "oneOf": [{ "const": "a" }, { "const": "b" }] }

loses its oneOf once resolved, so a and b aren't offered as completions.

Fix

Schemas are normalized on entry (updateSchema and stateExtensions) into the equivalent, draft-agnostic form:

{ "allOf": [{ "$ref": "#/$defs/key" }, { "oneOf": [...] }] }

Every draft handles this form the same way, and the existing allOf completion branch picks it up. Annotation keywords such as title and description stay at the top level, so property metadata is preserved.

Schemas whose $schema declares draft-04, -06 or -07 are left unchanged, because those drafts ignore keywords next to $ref. A nested $schema applies to its subschemas. Schemas without $schema are normalized, because json-schema-library treats them as 2020-12.

The normalizer only follows schema-bearing keywords (src/utils/schema-walk.ts):

  • It never rewrites literal data under const, enum, default or examples.
  • In maps like properties, it treats every entry as a schema, so a property named const is still normalized.

This replaces the getRawSchemaForPointer workaround, which only followed object properties and failed for a root-level $ref and for array item pointers.

Tests

  • A completion test with a $ref + oneOf fixture (an e-invoicing addons list).
  • Unit tests for the normalizer, covering data keywords, properties named like keywords, and declared drafts.
  • pnpm test (144 tests) and pnpm tsc pass.

🤖 Generated with Claude Code

samlown and others added 2 commits October 27, 2026 14:36
Move completion, hover and validation to the v10 compileSchema API.
v10 keeps sibling keywords next to $ref, so a $ref combined with a
oneOf of consts now offers those values as completions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
json-schema-library validates "if" against a copy of the validation
path so the nodes it evaluates aren't recorded. The path holds every
node visited so far and is never trimmed, so each "if" costs time
proportional to everything validated before it and validation is
quadratic in document size. Schemas that select definitions with
if/then took ~30s on a 380KB document and crashed the tab at 2MB.
v10 and v11 are both affected.

Compile schemas with drafts whose "if" validator uses the shared path
and truncates it afterwards, which discards the same entries without
the copy and keeps the dynamic scope for $dynamicRef and $recursiveRef.
All features now compile through utils/compile-schema.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b941cb5

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
codemirror-json-schema Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@netlify

netlify Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for codemirror-json-schema ready!

Name Link
🔨 Latest commit b941cb5
🔍 Latest deploy log https://app.netlify.com/projects/codemirror-json-schema/deploys/6ae0bbf8748e33982f11a488
😎 Deploy Preview https://deploy-preview-177--codemirror-json-schema.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

samlown and others added 3 commits October 27, 2026 14:56
json-schema-library v10 validators return errors only, and its
unevaluatedProperties re-runs "if" to find the evaluated branch, so the
wrapper's results match the library's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
json-schema-library resolves a $ref by replacing the node with its
target and dropping sibling keywords, the draft 4/7 behaviour, even when
the schema declares draft 2020-12. That discards constructs like

  { "$ref": "#/$defs/key", "oneOf": [ { "const": "..." }, ... ] }

where the oneOf carries the real constraint, so no value completions
are offered and the constraint isn't validated.

Normalize schemas on entry into the equivalent, draft-agnostic
{ "allOf": [ { "$ref": ... }, { ...siblings } ] }. Annotation keywords
(title, description, ...) stay at the top level so property metadata is
preserved. This replaces the getRawSchemaForPointer workaround, which
only followed object properties and broke on a root-level $ref and on
array item pointers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drafts 04, 06 and 07 ignore keywords next to $ref, so rewriting them
into an allOf changed what those schemas accept. Skip schemas, and
nested resources, whose $schema declares one of those drafts, matching
how json-schema-library picks a draft. Schemas without $schema keep
being normalized, as the library treats them as 2020-12.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@samlown
samlown force-pushed the upstream-pr/ref-sibling-keywords branch from f94e9c1 to b941cb5 Compare October 27, 2026 14:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant