@@ -128,4 +128,72 @@ describe('dereferenceLocalRefs', () => {
128128 } ;
129129 expect ( ( ) => dereferenceLocalRefs ( schema ) ) . toThrow ( / R e c u r s i v e s c h e m a d e t e c t e d / ) ;
130130 } ) ;
131+
132+ // The following tests cover real Zod v4 output patterns where $ref appears
133+ // with sibling keywords. Zod only produces metadata siblings (description,
134+ // title, default, etc.) — never schema nodes containing nested $ref.
135+ // These prove the sibling merge path handles all real-world scenarios.
136+
137+ test ( '$ref with multiple metadata siblings (Zod .meta() on registered type)' , ( ) => {
138+ const schema = {
139+ type : 'object' ,
140+ properties : {
141+ home : { $ref : '#/$defs/Address' , title : 'Home' , deprecated : true }
142+ } ,
143+ $defs : {
144+ Address : { type : 'object' , properties : { street : { type : 'string' } } }
145+ }
146+ } ;
147+ const result = dereferenceLocalRefs ( schema ) ;
148+ const home = ( result [ 'properties' ] as Record < string , Record < string , unknown > > ) [ 'home' ] ! ;
149+ expect ( home [ 'type' ] ) . toBe ( 'object' ) ;
150+ expect ( home [ 'title' ] ) . toBe ( 'Home' ) ;
151+ expect ( home [ 'deprecated' ] ) . toBe ( true ) ;
152+ expect ( JSON . stringify ( result ) ) . not . toContain ( '$ref' ) ;
153+ } ) ;
154+
155+ test ( '$ref with default value sibling (Zod .default() on registered type)' , ( ) => {
156+ const schema = {
157+ type : 'object' ,
158+ properties : {
159+ home : { $ref : '#/$defs/Address' , default : { street : '123 Main' } }
160+ } ,
161+ $defs : {
162+ Address : { type : 'object' , properties : { street : { type : 'string' } } }
163+ }
164+ } ;
165+ const result = dereferenceLocalRefs ( schema ) ;
166+ const home = ( result [ 'properties' ] as Record < string , Record < string , unknown > > ) [ 'home' ] ! ;
167+ expect ( home [ 'type' ] ) . toBe ( 'object' ) ;
168+ expect ( home [ 'default' ] ) . toEqual ( { street : '123 Main' } ) ;
169+ expect ( JSON . stringify ( result ) ) . not . toContain ( '$ref' ) ;
170+ } ) ;
171+
172+ test ( '$def referencing another $def (nested registered types)' , ( ) => {
173+ const schema = {
174+ type : 'object' ,
175+ properties : {
176+ employer : { $ref : '#/$defs/Company' , description : 'The company' }
177+ } ,
178+ $defs : {
179+ Address : { type : 'object' , properties : { street : { type : 'string' } } } ,
180+ Company : {
181+ type : 'object' ,
182+ properties : {
183+ name : { type : 'string' } ,
184+ hq : { $ref : '#/$defs/Address' }
185+ }
186+ }
187+ }
188+ } ;
189+ const result = dereferenceLocalRefs ( schema ) ;
190+ expect ( JSON . stringify ( result ) ) . not . toContain ( '$ref' ) ;
191+ expect ( JSON . stringify ( result ) ) . not . toContain ( '$defs' ) ;
192+ const employer = ( result [ 'properties' ] as Record < string , Record < string , unknown > > ) [ 'employer' ] ! ;
193+ expect ( employer [ 'description' ] ) . toBe ( 'The company' ) ;
194+ expect ( employer [ 'type' ] ) . toBe ( 'object' ) ;
195+ const hq = ( employer [ 'properties' ] as Record < string , Record < string , unknown > > ) [ 'hq' ] ! ;
196+ expect ( hq [ 'type' ] ) . toBe ( 'object' ) ;
197+ expect ( hq [ 'properties' ] ) . toEqual ( { street : { type : 'string' } } ) ;
198+ } ) ;
131199} ) ;
0 commit comments