Repository navigation
Conversation
🦋 Changeset detectedLatest commit: 5b7ade9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
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 |
fe59519 to
7d66075
Compare
Performance Benchmark (Lower is Faster)
|
8e52378 to
33578bd
Compare
33578bd to
2eaae41
Compare
743e369 to
e01e302
Compare
|
@cursor review |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e01e302. Configure here.
| message: `Security scheme \`${change.property}\` requires new scopes: ${quoted(added)}.`, | ||
| }); | ||
| }, | ||
| }); |
There was a problem hiding this comment.
AND security scheme addition missed
Medium Severity
Adding another scheme to an existing security requirement (AND) produces no verdict when the new scheme has no scopes. SecurityScopesAdded only reports extra scopes, and SecurityRequirementAdded only covers going from no authentication to some.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit e01e302. Configure here.
| if (change.kind === 'removed') | ||
| report({ message: `Media type \`${nameOf(change.node)}\` was removed.` }); | ||
| }, | ||
| }); |
There was a problem hiding this comment.
Removed maps skip child rules
High Severity
collectChanges reports a removed map once and does not visit its children. media-type-removed and property-removed only match those children, so deleting content or properties is rated patch even though removing the same members one by one is major.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit e01e302. Configure here.
| const before = acceptedTypes(fieldOf(base, 'type'), fieldOf(base, 'nullable')); | ||
| const after = acceptedTypes(fieldOf(revision, 'type'), fieldOf(revision, 'nullable')); | ||
| // An absent `type` accepts anything, so there is nothing to narrow or widen. | ||
| if (!before.length || !after.length) return; |
There was a problem hiding this comment.
Untyped schema type changes ignored
Medium Severity
SchemaTypeChanged returns early when either side omits type. Adding a type to an untyped request schema narrows what clients may send, and dropping a type from a response widens what they must handle. Both stay unjudged.
Reviewed by Cursor Bugbot for commit e01e302. Configure here.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Compare two API descriptions and report added, removed, and changed parts. Structural diff works for every supported spec type via the existing openapi-core type trees; breaking-change classification (breaking / warning / non-breaking) applies to OpenAPI 3.x. The diff engine lives entirely in the CLI package and consumes only the public @redocly/openapi-core API (walkDocument, type trees, bundle) — packages/core is untouched. Pipeline: collect each side into a flat stable-pointer map, two-pass compare into a change list, then classify with a polarity-aware lint-style rule registry (worst verdict wins). Supports stylish, json, markdown, and html output and a --fail-on CI gate. Marked [experimental]; 14 starter rules documented. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… verdicts Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ons, and path-param matching Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e case Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…iscriminated union
…d and --check-version
… recommended-diff preset
… recommended-diff and add --skip-rule
…-recommended and configure diff once per project
…pecification-agnostic
Match the two node maps up into a tree of pairs instead of computing a string key per
node and joining the two key sets. A pair is the identity, so the report key becomes a
label, the collision suffix stops being part of matching, and a change is addressed by
its pair rather than by a key and a node.
Everything a specification knows now sits behind `DiffSpec` in `diff/specs/`, so the
comparison itself names no node type. Rules gain lint's nested visitor keys:
`SchemaProperties: { Schema }` replaces a parent check in the rule body. The usage index
becomes one fixed-point pass over the reference graph instead of a class that recursed
per change.
The report formats move to the CLI, which is their only consumer, and the semver helper
gives way to the `semver` package already in its dependencies.
…nd sort changes in the report formats
… each diff rule its own file
…oded refs, and settle directions in linear time
… children by a segment alone, and derive diff families from the rule registry
…nAPI 3.0 nullable change as a type change
…gs, add the next-version format, and align the report formats with the rest of the CLI
…s formats, specifications, configuration and plugins
…writeOnly, whole-map removals and untyped schemas like a client would, and merge overlapping rules
… and diff rules in plugins
…de and diff trees, and trim the docs
b741b3c to
5b7ade9
Compare
📝 Recheck Summary
📋 Findings by rule🟡 technical-english/sentence-length: 0 errors, 11 warnings, 0 info Errors fail the full-tree check. Warnings and info are a worklist and do not block the PR. 📖 Readability of changed filesAutomated Readability Index (ARI): a U.S. grade level. Lower is easier to read.
|


What/Why/How?
Adds the experimental
redocly diffcommand.It compares two versions of an API description and gives each change an impact: the semver part that a release must bump (
major,minor, orpatch).For other description types, it lists the changes, but no rule judges them.
1on amajorchange.--fail-onsets another threshold.--check-versionfails ifinfo.versionis not bumped enough.stylish,json,markdown,html,github-actions,next-version.diffsection ofredocly.yamlsets the impact of each rule.diff-recommendedturns on all 27 rules, and--skip-ruleturns off a rule for one run.How it works
flowchart LR base["base.yaml"] --> baseTree["node tree"] revision["revision.yaml"] --> revisionTree["node tree"] baseTree --> compare["compareTrees"] revisionTree --> compare compare --> changes["changes"] changes --> judge["judgeChanges"] rules[("diff rules + impacts from redocly.yaml")] -.-> judge judge --> report["report format"]buildNodeTreewalks each document once, after the preprocessors.Each node keeps its type from the walker, and a
$refnode points to its target.compareTreespairs the two trees from the root down and lists what was added, removed, or modified.Nothing below an added or removed node is compared.
judgeChangesruns the diff rules on each change, and the highest impact wins.A change that no rule reports is
minorif something was added, andpatchotherwise.Pairing
Map entries pair by key, and list items by content.
Exact matches pair first.
Each other child pairs with the most alike child of the same type, if they are at least half alike (like git rename detection).
So a reordered list or a renamed key is one change, not a removal and an addition:
flowchart LR subgraph baseParameters [base parameters] baseLimit["0: limit, query"] baseSearch["1: search, query"] end subgraph revisionParameters [revision parameters] revisionSearch["0: search, query"] revisionLimits["1: limits, query"] end baseSearch -- same --> revisionSearch baseLimit -- "most alike: name changed" --> revisionLimitsdirections
The same change can break a request and be safe in a response.
A rule calls
getDirections()only when it needs the direction.The directions come from the nearest node that knows its direction (request body, parameters, responses,
readOnly/writeOnly, AsyncAPIaction), above the change or above any place inreferencedBy.Callbacks and webhooks swap the directions.
flowchart BT change["new enum value in Order.status"] --> order["Order schema"] order --> requestBody["POST /orders requestBody: request"] order -. referencedBy .-> response["GET /orders/{id} 200: response"]Here the directions are request and response, so
enum-values-addedreports the change asmajor.Diff rules
A diff rule is a visitor over changes.
It is keyed by node type and nested like a lint visitor (simplified):
Changes in core
diffDocuments()and the diff result types.node-tree/: the typed tree of a document, built by the walker.diff/:compareTrees,judgeChanges, directions, and the rules indiff/rules/.diff,oas3_0Diff,oas3_1Diff,oas3_2Diff, andasync3Diffsections, and thediff-recommendedruleset.Plugins can add diff rules, but this is not documented yet.
Reference
Testing
Screenshots (optional)
Check yourself
Security
Note
Medium Risk
Large new comparison and rule engine plus default config now includes
diff-recommended; misclassified impacts or bundle resolution gaps could affect CI gates, though the feature is marked experimental.Overview
Adds an experimental
redocly diff <base> <revision>command that bundles both API descriptions, compares them structurally, and reports additions, removals, and edits with a semver impact (patch,minor,major) per change.The comparison engine lives in
@redocly/openapi-core: it builds matched diff trees (identity-aware matching for parameters, paths,$reftargets, etc.), resolves request vs response direction (including callbacks/webhooks and AsyncAPIaction), runs a diff rule registry for OpenAPI 3.x and AsyncAPI 3, and exposesdiffDocuments. Governance is configured like lint viaextends: [diff-recommended], globaldiff/ per-specoas3_*Diff/async3Diffblocks, and plugindiffrules; default config now also extendsdiff-recommended.The CLI command adds report formats (
stylish,json,markdown,html,github-actions,next-version),--fail-on(defaultmajor), optional--check-versionagainstinfo.version, and--skip-rule. Documentation and a minor changeset cover both packages.Reviewed by Cursor Bugbot for commit e01e302. Bugbot is set up for automated code reviews on this repo. Configure here.