* Post-process OpenAPI schema to remove fields with default values from required arrays. * This fixes the issue where z.toJSONSchema() marks optional-with-default fields as required.
(schema: unknown)
| 13 | * Post-process OpenAPI schema to remove fields with default values from required arrays. |
| 14 | * This fixes the issue where z.toJSONSchema() marks optional-with-default fields as required. |
| 15 | */ |
| 16 | function removeDefaultsFromRequired(schema: unknown): unknown { |
| 17 | if (!schema || typeof schema !== 'object') return schema |
| 18 | |
| 19 | const obj = schema as Record<string, unknown> |
| 20 | |
| 21 | // If this is a schema object with properties and required |
| 22 | if ('properties' in obj && 'required' in obj && Array.isArray(obj.required)) { |
| 23 | const properties = obj.properties as Record<string, unknown> | undefined |
| 24 | const required = obj.required as string[] |
| 25 | |
| 26 | if (properties) { |
| 27 | // Remove any property from required if it has a default value |
| 28 | const newRequired = required.filter(key => { |
| 29 | const prop = properties[key] |
| 30 | if (prop && typeof prop === 'object' && 'default' in prop) return false |
| 31 | |
| 32 | return true |
| 33 | }) |
| 34 | |
| 35 | // Recursively process properties |
| 36 | const newProperties: Record<string, unknown> = {} |
| 37 | for (const [key, value] of Object.entries(properties)) |
| 38 | newProperties[key] = removeDefaultsFromRequired(value) |
| 39 | |
| 40 | return { |
| 41 | ...obj, |
| 42 | properties: newProperties, |
| 43 | required: newRequired.length > 0 ? newRequired : undefined, |
| 44 | } |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | const processComponents = (components: Record<string, unknown>): Record<string, unknown> => { |
| 49 | const newComponents: Record<string, unknown> = {} |
| 50 | for (const [compKey, compValue] of Object.entries(components)) |
| 51 | if (compKey === 'schemas' && typeof compValue === 'object' && compValue !== null) { |
| 52 | const schemas = compValue as Record<string, unknown> |
| 53 | const newSchemas: Record<string, unknown> = {} |
| 54 | for (const [schemaKey, schemaValue] of Object.entries(schemas)) |
| 55 | newSchemas[schemaKey] = removeDefaultsFromRequired(schemaValue) |
| 56 | newComponents[compKey] = newSchemas |
| 57 | } else { |
| 58 | newComponents[compKey] = removeDefaultsFromRequired(compValue) |
| 59 | } |
| 60 | return newComponents |
| 61 | } |
| 62 | |
| 63 | // Recursively process nested objects |
| 64 | const result: Record<string, unknown> = {} |
| 65 | for (const [key, value] of Object.entries(obj)) |
| 66 | if (key === 'paths' && typeof value === 'object' && value !== null) { |
| 67 | // Process paths object |
| 68 | const paths = value as Record<string, unknown> |
| 69 | const newPaths: Record<string, unknown> = {} |
| 70 | for (const [pathKey, pathValue] of Object.entries(paths)) |
| 71 | newPaths[pathKey] = removeDefaultsFromRequired(pathValue) |
| 72 |
no test coverage detected