diff --git a/.changeset/chilled-wolves-pump.md b/.changeset/chilled-wolves-pump.md new file mode 100644 index 0000000000..1802a812c1 --- /dev/null +++ b/.changeset/chilled-wolves-pump.md @@ -0,0 +1,5 @@ +--- +"@hey-api/openapi-ts": patch +--- + +parser: merge `default` keyword with `$ref` in OpenAPI 3.1 diff --git a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/valibot/default/valibot.gen.ts b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/valibot/default/valibot.gen.ts index badfc0b767..3bc01e4f18 100644 --- a/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/valibot/default/valibot.gen.ts +++ b/packages/openapi-ts-tests/main/test/__snapshots__/3.1.x/plugins/valibot/default/valibot.gen.ts @@ -1455,7 +1455,9 @@ export const vCallWithDefaultParametersData = v.object({ 'Error' ])), parameterModel: v.optional(v.union([ - vModelWithString, + v.optional(vModelWithString, { + prop: 'Hello World!' + }), v.null() ])) })) @@ -1473,7 +1475,9 @@ export const vCallWithDefaultOptionalParametersData = v.object({ 'Warning', 'Error' ])), - parameterModel: v.optional(vModelWithString) + parameterModel: v.optional(vModelWithString, { + prop: 'Hello World!' + }) })) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/default/zod.gen.ts index 96b8cd3542..2a97dcfe22 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/default/zod.gen.ts @@ -1486,7 +1486,9 @@ export const zCallWithDefaultParametersData = z.object({ 'Error' ])), parameterModel: z.optional(z.union([ - zModelWithString, + z._default(zModelWithString, { + prop: 'Hello World!' + }), z.null() ])) })) @@ -1504,7 +1506,9 @@ export const zCallWithDefaultOptionalParametersData = z.object({ 'Warning', 'Error' ])), - parameterModel: z.optional(zModelWithString) + parameterModel: z._default(z.optional(zModelWithString), { + prop: 'Hello World!' + }) })) }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/default/zod.gen.ts index 552a53bc63..e56c1943f4 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/default/zod.gen.ts @@ -1484,7 +1484,9 @@ export const zCallWithDefaultParametersData = z.object({ 'Error' ]).optional(), parameterModel: z.union([ - zModelWithString, + zModelWithString.default({ + prop: 'Hello World!' + }), z.null() ]).optional() }).optional() @@ -1502,7 +1504,9 @@ export const zCallWithDefaultOptionalParametersData = z.object({ 'Warning', 'Error' ]).optional(), - parameterModel: zModelWithString.optional() + parameterModel: zModelWithString.optional().default({ + prop: 'Hello World!' + }) }).optional() }); diff --git a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/default/zod.gen.ts index 3386046ff7..6279534dbd 100644 --- a/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/default/zod.gen.ts @@ -1486,7 +1486,9 @@ export const zCallWithDefaultParametersData = z.object({ 'Error' ])), parameterModel: z.optional(z.union([ - zModelWithString, + zModelWithString.default({ + prop: 'Hello World!' + }), z.null() ])) })) @@ -1504,7 +1506,9 @@ export const zCallWithDefaultOptionalParametersData = z.object({ 'Warning', 'Error' ])), - parameterModel: z.optional(zModelWithString) + parameterModel: z.optional(zModelWithString).default({ + prop: 'Hello World!' + }) })) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/default/zod.gen.ts index 398c91a445..5ffa612787 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/default/zod.gen.ts @@ -1486,7 +1486,9 @@ export const zCallWithDefaultParametersData = z.object({ 'Error' ])), parameterModel: z.optional(z.union([ - zModelWithString, + z._default(zModelWithString, { + prop: 'Hello World!' + }), z.null() ])) })) @@ -1504,7 +1506,9 @@ export const zCallWithDefaultOptionalParametersData = z.object({ 'Warning', 'Error' ])), - parameterModel: z.optional(zModelWithString) + parameterModel: z._default(z.optional(zModelWithString), { + prop: 'Hello World!' + }) })) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/defaults-with-ref-and-anyof/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/defaults-with-ref-and-anyof/zod.gen.ts new file mode 100644 index 0000000000..74f89f961c --- /dev/null +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/defaults-with-ref-and-anyof/zod.gen.ts @@ -0,0 +1,40 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import * as z from 'zod/mini'; + +export const zAudioFormat = z.enum([ + 'pcm_16bit_44.1khz', + 'pcm_16bit_24khz' +]); + +export const zLanguage = z.enum([ + 'en', + 'es', + 'fr' +]); + +export const zNestedConfig = z.object({ + model: z._default(z.string(), 'gpt-4'), + temperature: z._default(z.number(), 1) +}); + +export const zTestSchema = z.object({ + primitiveDefault: z._default(z.number(), 42), + refWithPrimitiveDefault: z._default(zAudioFormat, 'pcm_16bit_44.1khz'), + refWithObjectDefault: z._default(zNestedConfig, { + model: 'gpt-4', + temperature: 1 + }), + anyOfWithNullDefault: z._default(z.union([ + z.number(), + z.null() + ]), null), + anyOfWithRefAndNullDefault: z._default(z.union([ + zLanguage, + z.null() + ]), null), + optionalAnyOfWithDefault: z._default(z.optional(z.union([ + z.string(), + z.null() + ])), null) +}); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/default/zod.gen.ts index 291c230d83..49aa50ac5d 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/default/zod.gen.ts @@ -1484,7 +1484,9 @@ export const zCallWithDefaultParametersData = z.object({ 'Error' ]).optional(), parameterModel: z.union([ - zModelWithString, + zModelWithString.default({ + prop: 'Hello World!' + }), z.null() ]).optional() }).optional() @@ -1502,7 +1504,9 @@ export const zCallWithDefaultOptionalParametersData = z.object({ 'Warning', 'Error' ]).optional(), - parameterModel: zModelWithString.optional() + parameterModel: zModelWithString.optional().default({ + prop: 'Hello World!' + }) }).optional() }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/defaults-with-ref-and-anyof/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/defaults-with-ref-and-anyof/zod.gen.ts new file mode 100644 index 0000000000..48d5f8925a --- /dev/null +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/defaults-with-ref-and-anyof/zod.gen.ts @@ -0,0 +1,40 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { z } from 'zod/v3'; + +export const zAudioFormat = z.enum([ + 'pcm_16bit_44.1khz', + 'pcm_16bit_24khz' +]); + +export const zLanguage = z.enum([ + 'en', + 'es', + 'fr' +]); + +export const zNestedConfig = z.object({ + model: z.string().default('gpt-4'), + temperature: z.number().default(1) +}); + +export const zTestSchema = z.object({ + primitiveDefault: z.number().default(42), + refWithPrimitiveDefault: zAudioFormat.default('pcm_16bit_44.1khz'), + refWithObjectDefault: zNestedConfig.default({ + model: 'gpt-4', + temperature: 1 + }), + anyOfWithNullDefault: z.union([ + z.number(), + z.null() + ]).default(null), + anyOfWithRefAndNullDefault: z.union([ + zLanguage, + z.null() + ]).default(null), + optionalAnyOfWithDefault: z.union([ + z.string(), + z.null() + ]).optional().default(null) +}); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/default/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/default/zod.gen.ts index 747685731a..8bafc886b2 100644 --- a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/default/zod.gen.ts +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/default/zod.gen.ts @@ -1486,7 +1486,9 @@ export const zCallWithDefaultParametersData = z.object({ 'Error' ])), parameterModel: z.optional(z.union([ - zModelWithString, + zModelWithString.default({ + prop: 'Hello World!' + }), z.null() ])) })) @@ -1504,7 +1506,9 @@ export const zCallWithDefaultOptionalParametersData = z.object({ 'Warning', 'Error' ])), - parameterModel: z.optional(zModelWithString) + parameterModel: z.optional(zModelWithString).default({ + prop: 'Hello World!' + }) })) }); diff --git a/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/defaults-with-ref-and-anyof/zod.gen.ts b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/defaults-with-ref-and-anyof/zod.gen.ts new file mode 100644 index 0000000000..6a47287f0c --- /dev/null +++ b/packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/defaults-with-ref-and-anyof/zod.gen.ts @@ -0,0 +1,40 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { z } from 'zod'; + +export const zAudioFormat = z.enum([ + 'pcm_16bit_44.1khz', + 'pcm_16bit_24khz' +]); + +export const zLanguage = z.enum([ + 'en', + 'es', + 'fr' +]); + +export const zNestedConfig = z.object({ + model: z.string().default('gpt-4'), + temperature: z.number().default(1) +}); + +export const zTestSchema = z.object({ + primitiveDefault: z.number().default(42), + refWithPrimitiveDefault: zAudioFormat.default('pcm_16bit_44.1khz'), + refWithObjectDefault: zNestedConfig.default({ + model: 'gpt-4', + temperature: 1 + }), + anyOfWithNullDefault: z.union([ + z.number(), + z.null() + ]).default(null), + anyOfWithRefAndNullDefault: z.union([ + zLanguage, + z.null() + ]).default(null), + optionalAnyOfWithDefault: z.optional(z.union([ + z.string(), + z.null() + ])).default(null) +}); diff --git a/packages/openapi-ts-tests/zod/v4/test/3.1.x.test.ts b/packages/openapi-ts-tests/zod/v4/test/3.1.x.test.ts index c8d0d0b1c3..03877b95f9 100644 --- a/packages/openapi-ts-tests/zod/v4/test/3.1.x.test.ts +++ b/packages/openapi-ts-tests/zod/v4/test/3.1.x.test.ts @@ -42,6 +42,13 @@ for (const zodVersion of zodVersions) { description: 'generates correct array when items are oneOf array with single item', }, + { + config: createConfig({ + input: 'defaults-with-ref-and-anyof.json', + output: 'defaults-with-ref-and-anyof', + }), + description: 'preserves defaults with $ref and anyOf', + }, { config: createConfig({ input: 'enum-null.json', diff --git a/packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts b/packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts index 7a270dd398..fb4fbebcba 100644 --- a/packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts +++ b/packages/openapi-ts/src/openApi/3.1.x/parser/schema.ts @@ -382,6 +382,7 @@ const parseAllOf = ({ state: SchemaState; }): IR.SchemaObject => { let irSchema = initIrSchema({ schema }); + parseSchemaMeta({ irSchema, schema }); const schemaItems: Array = []; const schemaTypes = getSchemaTypes({ schema }); @@ -543,6 +544,7 @@ const parseAnyOf = ({ state: SchemaState; }): IR.SchemaObject => { let irSchema = initIrSchema({ schema }); + parseSchemaMeta({ irSchema, schema }); const schemaItems: Array = []; const schemaTypes = getSchemaTypes({ schema }); @@ -699,6 +701,7 @@ const parseOneOf = ({ state: SchemaState; }): IR.SchemaObject => { let irSchema = initIrSchema({ schema }); + parseSchemaMeta({ irSchema, schema }); let schemaItems: Array = []; const schemaTypes = getSchemaTypes({ schema }); @@ -817,6 +820,7 @@ const parseRef = ({ } let irSchema = initIrSchema({ schema }); + parseSchemaMeta({ irSchema, schema }); const irRefSchema: IR.SchemaObject = {}; diff --git a/specs/3.1.x/defaults-with-ref-and-anyof.json b/specs/3.1.x/defaults-with-ref-and-anyof.json new file mode 100644 index 0000000000..933e7f08ff --- /dev/null +++ b/specs/3.1.x/defaults-with-ref-and-anyof.json @@ -0,0 +1,76 @@ +{ + "openapi": "3.1.0", + "info": { + "title": "Defaults with $ref and anyOf", + "version": "1.0.0" + }, + "paths": {}, + "components": { + "schemas": { + "AudioFormat": { + "type": "string", + "enum": ["pcm_16bit_44.1khz", "pcm_16bit_24khz"] + }, + "Language": { + "type": "string", + "enum": ["en", "es", "fr"] + }, + "NestedConfig": { + "type": "object", + "properties": { + "model": { + "type": "string", + "default": "gpt-4" + }, + "temperature": { + "type": "number", + "default": 1 + } + }, + "required": ["model", "temperature"] + }, + "TestSchema": { + "type": "object", + "properties": { + "primitiveDefault": { + "type": "number", + "default": 42 + }, + "refWithPrimitiveDefault": { + "$ref": "#/components/schemas/AudioFormat", + "default": "pcm_16bit_44.1khz" + }, + "refWithObjectDefault": { + "$ref": "#/components/schemas/NestedConfig", + "default": { + "model": "gpt-4", + "temperature": 1 + } + }, + "anyOfWithNullDefault": { + "anyOf": [{ "type": "number" }, { "type": "null" }], + "default": null + }, + "anyOfWithRefAndNullDefault": { + "anyOf": [ + { "$ref": "#/components/schemas/Language" }, + { "type": "null" } + ], + "default": null + }, + "optionalAnyOfWithDefault": { + "anyOf": [{ "type": "string" }, { "type": "null" }], + "default": null + } + }, + "required": [ + "primitiveDefault", + "refWithPrimitiveDefault", + "refWithObjectDefault", + "anyOfWithNullDefault", + "anyOfWithRefAndNullDefault" + ] + } + } + } +}