From a005e55cd4f22efa92abe44addbdadacd872b0b9 Mon Sep 17 00:00:00 2001 From: BatLeDev Date: Mon, 9 Nov 2026 10:24:25 +0100 Subject: [PATCH] fix(utils): keep a property named $ref in makeLocalDefs makeLocalDefs treated every "$ref" key as a reference, including a property named "$ref" (the Reference Object of the OpenAPI 3.1 meta-schema), and crashed on value.startsWith. A JSON Schema $ref keyword is always a string, so non-string values are now walked as ordinary schema fragments. Schemas that built before only had string $ref values, so their output does not change. Co-Authored-By: Claude Opus 5.5 --- packages/utils/json-schema.test.ts | 19 +++++++++++++++++++ packages/utils/json-schema.ts | 5 +++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/utils/json-schema.test.ts b/packages/utils/json-schema.test.ts index 7c89c17d..6d9ed7d7 100644 --- a/packages/utils/json-schema.test.ts +++ b/packages/utils/json-schema.test.ts @@ -63,6 +63,25 @@ describe('json-schema utility functions', () => { }) }) + it('should keep a property named $ref when making local definitions', async () => { + const schema = makeLocalDefs({ + 'http://test.com/schema1': { + type: 'object', + properties: { + ref: { $ref: 'http://test.com/schema2' } + } + }, + 'http://test.com/schema2': { + type: 'object', + properties: { + $ref: { type: 'string', format: 'uri-reference' } + } + } + }, 'http://test.com/schema1') + assert.equal(schema.properties.ref.$ref, '#/$defs/schema2') + assert.deepEqual(schema.$defs.schema2.properties.$ref, { type: 'string', format: 'uri-reference' }) + }) + it('should transform absolute references to local definitions', async () => { const schema = makeLocalDefs({ 'http://test.com/schema1': { diff --git a/packages/utils/json-schema.ts b/packages/utils/json-schema.ts index 9252a4ed..81051fe8 100644 --- a/packages/utils/json-schema.ts +++ b/packages/utils/json-schema.ts @@ -227,7 +227,8 @@ const recurseLocalDefs = ( ) => { if (!schemaFragment) throw new Error('failed to resolve ' + schemaId) for (const [key, value] of Object.entries(schemaFragment)) { - if (key === '$ref') { + // a non-string $ref is a property named "$ref" (OpenAPI 3.1 meta-schema), not a reference + if (key === '$ref' && typeof value === 'string') { if (schemaId !== originalSchemaId || !value.startsWith('#')) { let fullId try { @@ -236,7 +237,7 @@ const recurseLocalDefs = ( throw new Error(`Failed to normalize URL ${value} from ${schemaId}`) } const [refSchemaId, refSchemaPath] = fullId.split('#') - const refName = value.split('/').pop() + const refName = value.slice(value.lastIndexOf('/') + 1) if (!localDefsSchemas[refName]) { let refSchema = schemas[refSchemaId] if (!refSchema) throw new Error('failed to resolve schema ' + refSchemaId)