Skip to content

Commit e4a247a

Browse files
RobertCraigiestainless-app[bot]
authored andcommitted
fix(helpers/zod): correct logic for adding root schema to definitions
1 parent 31e4afd commit e4a247a

File tree

2 files changed

+181
-4
lines changed

2 files changed

+181
-4
lines changed

src/_vendor/zod-to-json-schema/zodToJsonSchema.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,6 @@ const zodToJsonSchema = <Target extends Targets = 'jsonSchema7'>(
6161
main.title = title;
6262
}
6363

64-
const rootRefPath = name ? [...refs.basePath, refs.definitionPath, name].join('/') : null;
65-
6664
const combined: ReturnType<typeof zodToJsonSchema<Target>> =
6765
name === undefined ?
6866
definitions ?
@@ -74,13 +72,13 @@ const zodToJsonSchema = <Target extends Targets = 'jsonSchema7'>(
7472
: refs.nameStrategy === 'duplicate-ref' ?
7573
{
7674
...main,
77-
...(definitions || refs.seenRefs.has(rootRefPath!) ?
75+
...(definitions || refs.seenRefs.size ?
7876
{
7977
[refs.definitionPath]: {
8078
...definitions,
8179
// only actually duplicate the schema definition if it was ever referenced
8280
// otherwise the duplication is completely pointless
83-
...(refs.seenRefs.has(rootRefPath!) ? { [name]: main } : undefined),
81+
...(refs.seenRefs.size ? { [name]: main } : undefined),
8482
},
8583
}
8684
: undefined),

tests/lib/parser.test.ts

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,5 +267,184 @@ describe('.parse()', () => {
267267
}
268268
`);
269269
});
270+
271+
test('merged schemas', async () => {
272+
const personSchema = z.object({
273+
name: z.string(),
274+
phone_number: z.string().nullable(),
275+
});
276+
277+
const contactPersonSchema = z.object({
278+
person1: personSchema.merge(
279+
z.object({
280+
roles: z
281+
.array(z.enum(['parent', 'child', 'sibling', 'spouse', 'friend', 'other']))
282+
.describe('Any roles for which the contact is important, use other for custom roles'),
283+
description: z
284+
.string()
285+
.nullable()
286+
.describe('Open text for any other relevant information about what the contact does.'),
287+
}),
288+
),
289+
person2: personSchema.merge(
290+
z.object({
291+
differentField: z.string(),
292+
}),
293+
),
294+
});
295+
296+
expect(zodResponseFormat(contactPersonSchema, 'contactPerson').json_schema.schema)
297+
.toMatchInlineSnapshot(`
298+
{
299+
"$schema": "http://json-schema.org/draft-07/schema#",
300+
"additionalProperties": false,
301+
"definitions": {
302+
"contactPerson": {
303+
"additionalProperties": false,
304+
"properties": {
305+
"person1": {
306+
"additionalProperties": false,
307+
"properties": {
308+
"description": {
309+
"description": "Open text for any other relevant information about what the contact does.",
310+
"type": [
311+
"string",
312+
"null",
313+
],
314+
},
315+
"name": {
316+
"type": "string",
317+
},
318+
"phone_number": {
319+
"type": [
320+
"string",
321+
"null",
322+
],
323+
},
324+
"roles": {
325+
"description": "Any roles for which the contact is important, use other for custom roles",
326+
"items": {
327+
"enum": [
328+
"parent",
329+
"child",
330+
"sibling",
331+
"spouse",
332+
"friend",
333+
"other",
334+
],
335+
"type": "string",
336+
},
337+
"type": "array",
338+
},
339+
},
340+
"required": [
341+
"name",
342+
"phone_number",
343+
"roles",
344+
"description",
345+
],
346+
"type": "object",
347+
},
348+
"person2": {
349+
"additionalProperties": false,
350+
"properties": {
351+
"differentField": {
352+
"type": "string",
353+
},
354+
"name": {
355+
"$ref": "#/definitions/contactPerson/properties/person1/properties/name",
356+
},
357+
"phone_number": {
358+
"$ref": "#/definitions/contactPerson/properties/person1/properties/phone_number",
359+
},
360+
},
361+
"required": [
362+
"name",
363+
"phone_number",
364+
"differentField",
365+
],
366+
"type": "object",
367+
},
368+
},
369+
"required": [
370+
"person1",
371+
"person2",
372+
],
373+
"type": "object",
374+
},
375+
},
376+
"properties": {
377+
"person1": {
378+
"additionalProperties": false,
379+
"properties": {
380+
"description": {
381+
"description": "Open text for any other relevant information about what the contact does.",
382+
"type": [
383+
"string",
384+
"null",
385+
],
386+
},
387+
"name": {
388+
"type": "string",
389+
},
390+
"phone_number": {
391+
"type": [
392+
"string",
393+
"null",
394+
],
395+
},
396+
"roles": {
397+
"description": "Any roles for which the contact is important, use other for custom roles",
398+
"items": {
399+
"enum": [
400+
"parent",
401+
"child",
402+
"sibling",
403+
"spouse",
404+
"friend",
405+
"other",
406+
],
407+
"type": "string",
408+
},
409+
"type": "array",
410+
},
411+
},
412+
"required": [
413+
"name",
414+
"phone_number",
415+
"roles",
416+
"description",
417+
],
418+
"type": "object",
419+
},
420+
"person2": {
421+
"additionalProperties": false,
422+
"properties": {
423+
"differentField": {
424+
"type": "string",
425+
},
426+
"name": {
427+
"$ref": "#/definitions/contactPerson/properties/person1/properties/name",
428+
},
429+
"phone_number": {
430+
"$ref": "#/definitions/contactPerson/properties/person1/properties/phone_number",
431+
},
432+
},
433+
"required": [
434+
"name",
435+
"phone_number",
436+
"differentField",
437+
],
438+
"type": "object",
439+
},
440+
},
441+
"required": [
442+
"person1",
443+
"person2",
444+
],
445+
"type": "object",
446+
}
447+
`);
448+
});
270449
});
271450
});

0 commit comments

Comments
 (0)