fork of hey-api/openapi-ts because I need some additional things

Merge pull request #2946 from hey-api/copilot/fix-defaults-zod-generation

Fix missing defaults in Zod generation for $ref and anyOf schemas (OpenAPI 3.1.x only)

authored by

Lubos and committed by
GitHub
cb869f74 83d5749f

+254 -14
+5
.changeset/chilled-wolves-pump.md
··· 1 + --- 2 + "@hey-api/openapi-ts": patch 3 + --- 4 + 5 + parser: merge `default` keyword with `$ref` in OpenAPI 3.1
+6 -2
packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/valibot/default/valibot.gen.ts
··· 1455 1455 'Error' 1456 1456 ])), 1457 1457 parameterModel: v.optional(v.union([ 1458 - vModelWithString, 1458 + v.optional(vModelWithString, { 1459 + prop: 'Hello World!' 1460 + }), 1459 1461 v.null() 1460 1462 ])) 1461 1463 })) ··· 1473 1475 'Warning', 1474 1476 'Error' 1475 1477 ])), 1476 - parameterModel: v.optional(vModelWithString) 1478 + parameterModel: v.optional(vModelWithString, { 1479 + prop: 'Hello World!' 1480 + }) 1477 1481 })) 1478 1482 }); 1479 1483
+6 -2
packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/default/zod.gen.ts
··· 1486 1486 'Error' 1487 1487 ])), 1488 1488 parameterModel: z.optional(z.union([ 1489 - zModelWithString, 1489 + z._default(zModelWithString, { 1490 + prop: 'Hello World!' 1491 + }), 1490 1492 z.null() 1491 1493 ])) 1492 1494 })) ··· 1504 1506 'Warning', 1505 1507 'Error' 1506 1508 ])), 1507 - parameterModel: z.optional(zModelWithString) 1509 + parameterModel: z._default(z.optional(zModelWithString), { 1510 + prop: 'Hello World!' 1511 + }) 1508 1512 })) 1509 1513 }); 1510 1514
+6 -2
packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/default/zod.gen.ts
··· 1484 1484 'Error' 1485 1485 ]).optional(), 1486 1486 parameterModel: z.union([ 1487 - zModelWithString, 1487 + zModelWithString.default({ 1488 + prop: 'Hello World!' 1489 + }), 1488 1490 z.null() 1489 1491 ]).optional() 1490 1492 }).optional() ··· 1502 1504 'Warning', 1503 1505 'Error' 1504 1506 ]).optional(), 1505 - parameterModel: zModelWithString.optional() 1507 + parameterModel: zModelWithString.optional().default({ 1508 + prop: 'Hello World!' 1509 + }) 1506 1510 }).optional() 1507 1511 }); 1508 1512
+6 -2
packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/default/zod.gen.ts
··· 1486 1486 'Error' 1487 1487 ])), 1488 1488 parameterModel: z.optional(z.union([ 1489 - zModelWithString, 1489 + zModelWithString.default({ 1490 + prop: 'Hello World!' 1491 + }), 1490 1492 z.null() 1491 1493 ])) 1492 1494 })) ··· 1504 1506 'Warning', 1505 1507 'Error' 1506 1508 ])), 1507 - parameterModel: z.optional(zModelWithString) 1509 + parameterModel: z.optional(zModelWithString).default({ 1510 + prop: 'Hello World!' 1511 + }) 1508 1512 })) 1509 1513 }); 1510 1514
+6 -2
packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/default/zod.gen.ts
··· 1486 1486 'Error' 1487 1487 ])), 1488 1488 parameterModel: z.optional(z.union([ 1489 - zModelWithString, 1489 + z._default(zModelWithString, { 1490 + prop: 'Hello World!' 1491 + }), 1490 1492 z.null() 1491 1493 ])) 1492 1494 })) ··· 1504 1506 'Warning', 1505 1507 'Error' 1506 1508 ])), 1507 - parameterModel: z.optional(zModelWithString) 1509 + parameterModel: z._default(z.optional(zModelWithString), { 1510 + prop: 'Hello World!' 1511 + }) 1508 1512 })) 1509 1513 }); 1510 1514
+40
packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/defaults-with-ref-and-anyof/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import * as z from 'zod/mini'; 4 + 5 + export const zAudioFormat = z.enum([ 6 + 'pcm_16bit_44.1khz', 7 + 'pcm_16bit_24khz' 8 + ]); 9 + 10 + export const zLanguage = z.enum([ 11 + 'en', 12 + 'es', 13 + 'fr' 14 + ]); 15 + 16 + export const zNestedConfig = z.object({ 17 + model: z._default(z.string(), 'gpt-4'), 18 + temperature: z._default(z.number(), 1) 19 + }); 20 + 21 + export const zTestSchema = z.object({ 22 + primitiveDefault: z._default(z.number(), 42), 23 + refWithPrimitiveDefault: z._default(zAudioFormat, 'pcm_16bit_44.1khz'), 24 + refWithObjectDefault: z._default(zNestedConfig, { 25 + model: 'gpt-4', 26 + temperature: 1 27 + }), 28 + anyOfWithNullDefault: z._default(z.union([ 29 + z.number(), 30 + z.null() 31 + ]), null), 32 + anyOfWithRefAndNullDefault: z._default(z.union([ 33 + zLanguage, 34 + z.null() 35 + ]), null), 36 + optionalAnyOfWithDefault: z._default(z.optional(z.union([ 37 + z.string(), 38 + z.null() 39 + ])), null) 40 + });
+6 -2
packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/default/zod.gen.ts
··· 1484 1484 'Error' 1485 1485 ]).optional(), 1486 1486 parameterModel: z.union([ 1487 - zModelWithString, 1487 + zModelWithString.default({ 1488 + prop: 'Hello World!' 1489 + }), 1488 1490 z.null() 1489 1491 ]).optional() 1490 1492 }).optional() ··· 1502 1504 'Warning', 1503 1505 'Error' 1504 1506 ]).optional(), 1505 - parameterModel: zModelWithString.optional() 1507 + parameterModel: zModelWithString.optional().default({ 1508 + prop: 'Hello World!' 1509 + }) 1506 1510 }).optional() 1507 1511 }); 1508 1512
+40
packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/defaults-with-ref-and-anyof/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod/v3'; 4 + 5 + export const zAudioFormat = z.enum([ 6 + 'pcm_16bit_44.1khz', 7 + 'pcm_16bit_24khz' 8 + ]); 9 + 10 + export const zLanguage = z.enum([ 11 + 'en', 12 + 'es', 13 + 'fr' 14 + ]); 15 + 16 + export const zNestedConfig = z.object({ 17 + model: z.string().default('gpt-4'), 18 + temperature: z.number().default(1) 19 + }); 20 + 21 + export const zTestSchema = z.object({ 22 + primitiveDefault: z.number().default(42), 23 + refWithPrimitiveDefault: zAudioFormat.default('pcm_16bit_44.1khz'), 24 + refWithObjectDefault: zNestedConfig.default({ 25 + model: 'gpt-4', 26 + temperature: 1 27 + }), 28 + anyOfWithNullDefault: z.union([ 29 + z.number(), 30 + z.null() 31 + ]).default(null), 32 + anyOfWithRefAndNullDefault: z.union([ 33 + zLanguage, 34 + z.null() 35 + ]).default(null), 36 + optionalAnyOfWithDefault: z.union([ 37 + z.string(), 38 + z.null() 39 + ]).optional().default(null) 40 + });
+6 -2
packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/default/zod.gen.ts
··· 1486 1486 'Error' 1487 1487 ])), 1488 1488 parameterModel: z.optional(z.union([ 1489 - zModelWithString, 1489 + zModelWithString.default({ 1490 + prop: 'Hello World!' 1491 + }), 1490 1492 z.null() 1491 1493 ])) 1492 1494 })) ··· 1504 1506 'Warning', 1505 1507 'Error' 1506 1508 ])), 1507 - parameterModel: z.optional(zModelWithString) 1509 + parameterModel: z.optional(zModelWithString).default({ 1510 + prop: 'Hello World!' 1511 + }) 1508 1512 })) 1509 1513 }); 1510 1514
+40
packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/defaults-with-ref-and-anyof/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod'; 4 + 5 + export const zAudioFormat = z.enum([ 6 + 'pcm_16bit_44.1khz', 7 + 'pcm_16bit_24khz' 8 + ]); 9 + 10 + export const zLanguage = z.enum([ 11 + 'en', 12 + 'es', 13 + 'fr' 14 + ]); 15 + 16 + export const zNestedConfig = z.object({ 17 + model: z.string().default('gpt-4'), 18 + temperature: z.number().default(1) 19 + }); 20 + 21 + export const zTestSchema = z.object({ 22 + primitiveDefault: z.number().default(42), 23 + refWithPrimitiveDefault: zAudioFormat.default('pcm_16bit_44.1khz'), 24 + refWithObjectDefault: zNestedConfig.default({ 25 + model: 'gpt-4', 26 + temperature: 1 27 + }), 28 + anyOfWithNullDefault: z.union([ 29 + z.number(), 30 + z.null() 31 + ]).default(null), 32 + anyOfWithRefAndNullDefault: z.union([ 33 + zLanguage, 34 + z.null() 35 + ]).default(null), 36 + optionalAnyOfWithDefault: z.optional(z.union([ 37 + z.string(), 38 + z.null() 39 + ])).default(null) 40 + });
+7
packages/openapi-ts-tests/zod/v4/test/3.1.x.test.ts
··· 44 44 }, 45 45 { 46 46 config: createConfig({ 47 + input: 'defaults-with-ref-and-anyof.json', 48 + output: 'defaults-with-ref-and-anyof', 49 + }), 50 + description: 'preserves defaults with $ref and anyOf', 51 + }, 52 + { 53 + config: createConfig({ 47 54 input: 'enum-null.json', 48 55 output: 'enum-null', 49 56 }),
+4
packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts
··· 382 382 state: SchemaState; 383 383 }): IR.SchemaObject => { 384 384 let irSchema = initIrSchema({ schema }); 385 + parseSchemaMeta({ irSchema, schema }); 385 386 386 387 const schemaItems: Array<IR.SchemaObject> = []; 387 388 const schemaTypes = getSchemaTypes({ schema }); ··· 543 544 state: SchemaState; 544 545 }): IR.SchemaObject => { 545 546 let irSchema = initIrSchema({ schema }); 547 + parseSchemaMeta({ irSchema, schema }); 546 548 547 549 const schemaItems: Array<IR.SchemaObject> = []; 548 550 const schemaTypes = getSchemaTypes({ schema }); ··· 699 701 state: SchemaState; 700 702 }): IR.SchemaObject => { 701 703 let irSchema = initIrSchema({ schema }); 704 + parseSchemaMeta({ irSchema, schema }); 702 705 703 706 let schemaItems: Array<IR.SchemaObject> = []; 704 707 const schemaTypes = getSchemaTypes({ schema }); ··· 817 820 } 818 821 819 822 let irSchema = initIrSchema({ schema }); 823 + parseSchemaMeta({ irSchema, schema }); 820 824 821 825 const irRefSchema: IR.SchemaObject = {}; 822 826
+76
specs/3.1.x/defaults-with-ref-and-anyof.json
··· 1 + { 2 + "openapi": "3.1.0", 3 + "info": { 4 + "title": "Defaults with $ref and anyOf", 5 + "version": "1.0.0" 6 + }, 7 + "paths": {}, 8 + "components": { 9 + "schemas": { 10 + "AudioFormat": { 11 + "type": "string", 12 + "enum": ["pcm_16bit_44.1khz", "pcm_16bit_24khz"] 13 + }, 14 + "Language": { 15 + "type": "string", 16 + "enum": ["en", "es", "fr"] 17 + }, 18 + "NestedConfig": { 19 + "type": "object", 20 + "properties": { 21 + "model": { 22 + "type": "string", 23 + "default": "gpt-4" 24 + }, 25 + "temperature": { 26 + "type": "number", 27 + "default": 1 28 + } 29 + }, 30 + "required": ["model", "temperature"] 31 + }, 32 + "TestSchema": { 33 + "type": "object", 34 + "properties": { 35 + "primitiveDefault": { 36 + "type": "number", 37 + "default": 42 38 + }, 39 + "refWithPrimitiveDefault": { 40 + "$ref": "#/components/schemas/AudioFormat", 41 + "default": "pcm_16bit_44.1khz" 42 + }, 43 + "refWithObjectDefault": { 44 + "$ref": "#/components/schemas/NestedConfig", 45 + "default": { 46 + "model": "gpt-4", 47 + "temperature": 1 48 + } 49 + }, 50 + "anyOfWithNullDefault": { 51 + "anyOf": [{ "type": "number" }, { "type": "null" }], 52 + "default": null 53 + }, 54 + "anyOfWithRefAndNullDefault": { 55 + "anyOf": [ 56 + { "$ref": "#/components/schemas/Language" }, 57 + { "type": "null" } 58 + ], 59 + "default": null 60 + }, 61 + "optionalAnyOfWithDefault": { 62 + "anyOf": [{ "type": "string" }, { "type": "null" }], 63 + "default": null 64 + } 65 + }, 66 + "required": [ 67 + "primitiveDefault", 68 + "refWithPrimitiveDefault", 69 + "refWithObjectDefault", 70 + "anyOfWithNullDefault", 71 + "anyOfWithRefAndNullDefault" 72 + ] 73 + } 74 + } 75 + } 76 + }