diff --git a/packages/downgrader/src/v3.1-to-v3.0.ts b/packages/downgrader/src/v3.1-to-v3.0.ts index 2d99ccb..3d83809 100644 --- a/packages/downgrader/src/v3.1-to-v3.0.ts +++ b/packages/downgrader/src/v3.1-to-v3.0.ts @@ -1736,19 +1736,17 @@ function convertDocument(value: unknown, ctx: Context): unknown { return convertObject(value, ctx, DOCUMENT_FIELDS, finishDocument) } -// A chain of bare `$ref`s from `schema` that comes back to it. +// A chain of `$ref`s from `schema` that comes back to it. 3.0 has no `$ref` +// with siblings, so each is bare. function leadsBack(schema: Record, ctx: Context): boolean { - const seen = new Set() - for (let next: unknown = schema; isRecord(next) && Object.keys(next).length === 1;) { - const ref = getRef(next) - if (ref === undefined || seen.has(next)) { - return false - } - seen.add(next) - next = resolvePointer(ref, ctx) + const seen = new Set() + for (let ref = getRef(schema); ref !== undefined && !seen.has(ref);) { + seen.add(ref) + const next = resolvePointer(ref, ctx) if (next === schema) { return true } + ref = getRef(next) } return false } diff --git a/packages/downgrader/tests/v3.1-to-v3.0.test.ts b/packages/downgrader/tests/v3.1-to-v3.0.test.ts index 73889a3..87e086b 100644 --- a/packages/downgrader/tests/v3.1-to-v3.0.test.ts +++ b/packages/downgrader/tests/v3.1-to-v3.0.test.ts @@ -1403,6 +1403,17 @@ describe('downgradeSchemaV31ToV30', () => { expect(schemas({ A: { anyOf: [ref('A'), ref('A'), { type: 'null' }] } })).toEqual({ A: { anyOf: [ref('A'), onlyNull] }, }) + // A cycle of objects in the target converts. + const cyclic: Record = { anyOf: [{ type: 'null' }] } + cyclic.anyOf.unshift({ anyOf: [cyclic, { type: 'null' }] }) + expect(schemas({ A: pair('B'), B: cyclic })?.A).toEqual(ref('B')) + // A `$ref` whose target moves in 3.0 leads nowhere. + expect( + schemas({ + A: pair('B/properties/a'), + B: { type: ['object', 'string'], properties: { a: { type: 'null' } } }, + })?.A, + ).toEqual(ref('B/properties/a')) }) it('merges a branch in full where a cycle meets it before its conversion is done', () => {