From 7f1468832fddf45c63b1ff1aec10130b10f39156 Mon Sep 17 00:00:00 2001 From: Dmitrii Donskoy Date: Wed, 29 Oct 2025 02:48:21 +0300 Subject: [PATCH 1/2] fix(zod): handle falsy default values in schema generation Fixed a bug where falsy default values (false, 0, '', null) were not properly applied to generated Zod schemas. The condition used `schema.default` which falsely treats falsy values as undefined. Changed the check to `schema.default !== undefined` to properly detect when a default value is set, regardless of its truthiness. Added test coverage for: - default: true - default: false - no default (undefined) Fixes #2222 --- packages/zod/src/index.ts | 2 +- packages/zod/src/zod.test.ts | 84 +++++++++++++++++++++++++++++++++--- 2 files changed, 79 insertions(+), 7 deletions(-) diff --git a/packages/zod/src/index.ts b/packages/zod/src/index.ts index b75b5cc2d..4f35ad555 100644 --- a/packages/zod/src/index.ts +++ b/packages/zod/src/index.ts @@ -584,7 +584,7 @@ export const generateZodValidationSchemaDefinition = ( } } - if (!required && schema.default) { + if (!required && schema.default !== undefined) { functions.push(['default', defaultVarName]); } else if (!required && nullable) { functions.push(['nullish', undefined]); diff --git a/packages/zod/src/zod.test.ts b/packages/zod/src/zod.test.ts index 6f5a92c3e..6fead7a72 100644 --- a/packages/zod/src/zod.test.ts +++ b/packages/zod/src/zod.test.ts @@ -434,7 +434,7 @@ describe('generateZodValidationSchemaDefinition`', () => { expect(parsed.consts).toBe('export const testNumberDefaultDefault = 42;'); }); - it('generates a default value for a boolean schema', () => { + it('generates a default value for a boolean schema with default: true', () => { const schemaWithBooleanDefault: SchemaObject30 = { type: 'boolean', default: true, @@ -443,7 +443,7 @@ describe('generateZodValidationSchemaDefinition`', () => { const result = generateZodValidationSchemaDefinition( schemaWithBooleanDefault, context, - 'testBooleanDefault', + 'testBooleanDefaultTrue', false, false, { required: false }, @@ -452,9 +452,9 @@ describe('generateZodValidationSchemaDefinition`', () => { expect(result).toEqual({ functions: [ ['boolean', undefined], - ['default', 'testBooleanDefaultDefault'], + ['default', 'testBooleanDefaultTrueDefault'], ], - consts: ['export const testBooleanDefaultDefault = true;'], + consts: ['export const testBooleanDefaultTrueDefault = true;'], }); const parsed = parseZodValidationSchemaDefinition( @@ -465,13 +465,85 @@ describe('generateZodValidationSchemaDefinition`', () => { false, ); expect(parsed.zod).toBe( - 'zod.boolean().default(testBooleanDefaultDefault)', + 'zod.boolean().default(testBooleanDefaultTrueDefault)', ); expect(parsed.consts).toBe( - 'export const testBooleanDefaultDefault = true;', + 'export const testBooleanDefaultTrueDefault = true;', ); }); + it('generates a default value for a boolean schema with default: false', () => { + const schemaWithBooleanDefault: SchemaObject30 = { + type: 'boolean', + default: false, + }; + + const result = generateZodValidationSchemaDefinition( + schemaWithBooleanDefault, + context, + 'testBooleanDefaultFalse', + false, + false, + { required: false }, + ); + + expect(result).toEqual({ + functions: [ + ['boolean', undefined], + ['default', 'testBooleanDefaultFalseDefault'], + ], + consts: ['export const testBooleanDefaultFalseDefault = false;'], + }); + + const parsed = parseZodValidationSchemaDefinition( + result, + context, + false, + false, + false, + ); + expect(parsed.zod).toBe( + 'zod.boolean().default(testBooleanDefaultFalseDefault)', + ); + expect(parsed.consts).toBe( + 'export const testBooleanDefaultFalseDefault = false;', + ); + }); + + it('generates a boolean schema without default (undefined)', () => { + const schemaWithoutDefault: SchemaObject30 = { + type: 'boolean', + // default property is undefined (not set) + }; + + const result = generateZodValidationSchemaDefinition( + schemaWithoutDefault, + context, + 'testBooleanNoDefault', + false, + false, + { required: false }, + ); + + expect(result).toEqual({ + functions: [ + ['boolean', undefined], + ['optional', undefined], + ], + consts: [], + }); + + const parsed = parseZodValidationSchemaDefinition( + result, + context, + false, + false, + false, + ); + expect(parsed.zod).toBe('zod.boolean().optional()'); + expect(parsed.consts).toBe(''); + }); + it('generates a default value for an array schema', () => { const schemaWithArrayDefault: SchemaObject30 = { type: 'array', From beaa83447c310d8249490c3b257fd8a50f4005f5 Mon Sep 17 00:00:00 2001 From: Dmitrii Donskoy Date: Wed, 29 Oct 2025 03:25:24 +0300 Subject: [PATCH 2/2] feature #2332: Add support for recursion/circular references in Zod --- packages/zod/src/index.ts | 282 ++++++++++++++++++---- packages/zod/src/zod.test.ts | 449 +++++++++++++++++++++++++++++++++++ 2 files changed, 688 insertions(+), 43 deletions(-) diff --git a/packages/zod/src/index.ts b/packages/zod/src/index.ts index 4f35ad555..75525b51c 100644 --- a/packages/zod/src/index.ts +++ b/packages/zod/src/index.ts @@ -179,6 +179,22 @@ export const generateZodValidationSchemaDefinition = ( ): ZodValidationSchemaDefinition => { if (!schema) return { functions: [], consts: [] }; + // Check for circular reference marker + // Check if schema is an object with __circular__ marker + if ( + typeof schema === 'object' && + schema !== null && + '__circular__' in schema && + (schema as any).__circular__ + ) { + const refName = (schema as any).__refName__; + // Return a lazy reference that will be resolved during parsing + return { + functions: [['circularRef', refName]], + consts: [], + }; + } + const consts: string[] = []; const constsCounter = typeof constsUniqueCounter[name] === 'number' @@ -475,18 +491,43 @@ export const generateZodValidationSchemaDefinition = ( functions.push([ objectType, Object.keys(schema.properties) - .map((key) => ({ - [key]: generateZodValidationSchemaDefinition( - schema.properties?.[key] as any, - context, - camel(`${name}-${key}`), - strict, - isZodV4, - { - required: schema.required?.includes(key), - }, - ), - })) + .map((key) => { + const propValue = schema.properties?.[key] as any; + // Check if this property has a circular reference marker + if ( + typeof propValue === 'object' && + propValue !== null && + '__circular__' in propValue && + propValue.__circular__ + ) { + // Return a lazy reference marker for this property + const isRequired = schema.required?.includes(key); + const propertyFunctions: [string, any][] = [ + ['circularRef', propValue.__refName__], + ]; + if (!isRequired) { + propertyFunctions.push(['optional', undefined]); + } + return { + [key]: { + functions: propertyFunctions, + consts: [], + }, + }; + } + return { + [key]: generateZodValidationSchemaDefinition( + propValue, + context, + camel(`${name}-${key}`), + strict, + isZodV4, + { + required: schema.required?.includes(key), + }, + ), + }; + }) .reduce((acc, curr) => ({ ...acc, ...curr }), {}), ]); @@ -684,22 +725,65 @@ export const parseZodValidationSchemaDefinition = ( return `zod.${objectType}({ ${Object.entries(args) .map(([key, schema]) => { - const value = (schema as ZodValidationSchemaDefinition).functions - .map(parseProperty) - .join(''); - consts += (schema as ZodValidationSchemaDefinition).consts.join('\n'); - return ` "${key}": ${value.startsWith('.') ? 'zod' : ''}${value}`; + const schemaDef = schema as ZodValidationSchemaDefinition; + + // Check if this property has a circular reference + const hasCircularRef = schemaDef.functions.some( + ([fnName]) => fnName === 'circularRef', + ); + + consts += schemaDef.consts.join('\n'); + + if (hasCircularRef) { + // Split functions: everything before circularRef, circularRef itself, and everything after + const circularRefIndex = schemaDef.functions.findIndex( + ([fn]) => fn === 'circularRef', + ); + const afterRef = schemaDef.functions + .slice(circularRefIndex + 1) + .map(parseProperty) + .join(''); + const refValue = parseProperty(schemaDef.functions[circularRefIndex]); + + // Wrap in lazy and apply modifiers after + return ` "${key}": zod.lazy(() => ${refValue})${afterRef}`; + } + + // Normal processing + const value = schemaDef.functions.map(parseProperty).join(''); + const valueWithZod = value.startsWith('.') ? `zod${value}` : value; + return ` "${key}": ${valueWithZod}`; }) .join(',\n')} })`; } if (fn === 'array') { const value = args.functions.map(parseProperty).join(''); + + // Check if the array items contain a circular reference + const hasCircularRef = args.functions.some( + ([fnName]) => fnName === 'circularRef', + ); + if (typeof args.consts === 'string') { consts += args.consts; } else if (Array.isArray(args.consts)) { consts += args.consts.join('\n'); } + + // If circular reference, wrap in lazy + if (hasCircularRef) { + const circularRefIndex = args.functions.findIndex( + ([fn]) => fn === 'circularRef', + ); + const afterRef = args.functions + .slice(circularRefIndex + 1) + .map(parseProperty) + .join(''); + const refValue = parseProperty(args.functions[circularRefIndex]); + return `.array(zod.lazy(() => ${refValue})${afterRef})`; + } + return `.array(${value.startsWith('.') ? 'zod' : ''}${value})`; } @@ -718,6 +802,14 @@ ${Object.entries(args) if (fn === 'rest') { return `.rest(zod${(args as ZodValidationSchemaDefinition).functions.map(parseProperty)})`; } + + if (fn === 'circularRef') { + // Extract the schema name from the reference (e.g., "#/components/schemas/Node" -> "Node") + const refName = args as string; + const schemaName = refName.split('/').pop() || ''; + return schemaName; + } + const shouldCoerceType = coerceTypes && (Array.isArray(coerceTypes) @@ -764,10 +856,20 @@ const deferenceScalar = (value: any, context: ContextSpecs): unknown => { const deference = ( schema: SchemaObject | ReferenceObject, context: ContextSpecs, -): SchemaObject => { + currentSchemaName?: string, +): SchemaObject | { __circular__: true; __refName__: string } => { const refName = '$ref' in schema ? schema.$ref : undefined; - if (refName && context.parents?.includes(refName)) { - return {}; + + // Check for circular reference - either in parents or same schema reference + const isCircular = + refName && + (context.parents?.includes(refName) || + (currentSchemaName && + refName === `#/components/schemas/${currentSchemaName}`)); + + if (isCircular) { + // Return a marker schema to indicate circular reference + return { __circular__: true, __refName__: refName } as any; } const childContext: ContextSpecs = { @@ -793,16 +895,22 @@ const deference = ( return Object.entries(resolvedSchema).reduce((acc, [key, value]) => { if (key === 'properties' && isObject(value)) { - acc[key] = Object.entries(value).reduce>( - (props, [propKey, propSchema]) => { - props[propKey] = deference( - propSchema as SchemaObject | ReferenceObject, - resolvedContext, - ); - return props; - }, - {}, - ); + acc[key] = Object.entries(value as Record).reduce< + Record< + string, + SchemaObject | { __circular__: true; __refName__: string } + > + >((props, [propKey, propSchema]) => { + const result = deference( + propSchema as SchemaObject | ReferenceObject, + resolvedContext, + context.parents?.[context.parents.length - 1] + ?.replace('#/components/schemas/', '') + .replace(/.*\//, ''), + ) as any; + props[propKey] = result; + return props; + }, {}); } else if (key === 'default' || key === 'example' || key === 'examples') { acc[key] = value; } else { @@ -862,22 +970,43 @@ const parseBodyAndResponse = ({ const resolvedJsonSchema = deference(schema, context); + // Check for circular reference marker + if ( + '__circular__' in resolvedJsonSchema && + (resolvedJsonSchema as any).__circular__ + ) { + // This is a circular reference, return it for processing + return { + input: generateZodValidationSchemaDefinition( + resolvedJsonSchema as any, + context, + name, + strict, + isZodV4, + { required: true }, + ), + isArray: false, + }; + } + // keep the same behaviour for array - if (resolvedJsonSchema.items) { + if ((resolvedJsonSchema as SchemaObject).items) { const min = - resolvedJsonSchema.minimum ?? - resolvedJsonSchema.minLength ?? - resolvedJsonSchema.minItems; + (resolvedJsonSchema as SchemaObject).minimum ?? + (resolvedJsonSchema as SchemaObject).minLength ?? + (resolvedJsonSchema as SchemaObject).minItems; const max = - resolvedJsonSchema.maximum ?? - resolvedJsonSchema.maxLength ?? - resolvedJsonSchema.maxItems; + (resolvedJsonSchema as SchemaObject).maximum ?? + (resolvedJsonSchema as SchemaObject).maxLength ?? + (resolvedJsonSchema as SchemaObject).maxItems; return { input: generateZodValidationSchemaDefinition( parseType === 'body' - ? removeReadOnlyProperties(resolvedJsonSchema.items as SchemaObject) - : (resolvedJsonSchema.items as SchemaObject), + ? removeReadOnlyProperties( + (resolvedJsonSchema as SchemaObject).items as SchemaObject, + ) + : ((resolvedJsonSchema as SchemaObject).items as SchemaObject), context, name, strict, @@ -897,8 +1026,8 @@ const parseBodyAndResponse = ({ return { input: generateZodValidationSchemaDefinition( parseType === 'body' - ? removeReadOnlyProperties(resolvedJsonSchema) - : resolvedJsonSchema, + ? removeReadOnlyProperties(resolvedJsonSchema as SchemaObject) + : (resolvedJsonSchema as SchemaObject), context, name, strict, @@ -973,7 +1102,10 @@ const parseParameters = ({ } const schema = deference(parameter.schema, context); - schema.description = parameter.description; + // Check for circular reference marker before accessing properties + if (!('__circular__' in schema && (schema as any).__circular__)) { + (schema as SchemaObject).description = parameter.description; + } const mapStrict = { path: strict.param, @@ -1233,12 +1365,75 @@ const generateZodRoute = async ( ), ); + // Collect all circular references used in the generated code + const allInputs = [ + inputParams, + inputQueryParams, + inputHeaders, + inputBody, + ...inputResponses, + ]; + const allCircularRefs = new Set(); + allInputs.forEach((input) => { + if (input?.zod && typeof input.zod === 'string') { + const regex = /zod\.lazy\(\(\) => (\w+)\)/g; + let match; + while ((match = regex.exec(input.zod)) !== null) { + allCircularRefs.add(match[1]); + } + } + }); + + // Generate schema definitions for circular references + const circularSchemaDefs: string[] = []; + allCircularRefs.forEach((schemaName) => { + const schemaNamePascal = pascal(schemaName); + // Find the schema in the openapi spec + const schema = + context.specs[context.specKey].components?.schemas?.[schemaNamePascal]; + if (schema && typeof schema === 'object' && 'properties' in schema) { + // First, dereference the schema to get markers for circular refs + const refFullPath = `#/components/schemas/${schemaNamePascal}`; + const schemaContext = { ...context, parents: [refFullPath] }; + + // Deference the schema to get markers for circular references + const deferenceSchema = deference( + schema as SchemaObject, + schemaContext, + schemaNamePascal, + ); + + // Generate Zod schema from the deference'd schema + const zodSchema = generateZodValidationSchemaDefinition( + deferenceSchema as SchemaObject, + schemaContext, + schemaNamePascal, + false, + isZodV4, + { required: true }, + ); + const parsed = parseZodValidationSchemaDefinition( + zodSchema, + schemaContext, + false, + false, + isZodV4, + ); + if (parsed.zod) { + circularSchemaDefs.push( + `export const ${schemaNamePascal} = ${parsed.zod};`, + ); + } + } + }); + if ( !inputParams.zod && !inputQueryParams.zod && !inputHeaders.zod && !inputBody.zod && - !inputResponses.some((inputResponse) => inputResponse.zod) + !inputResponses.some((inputResponse) => inputResponse.zod) && + circularSchemaDefs.length === 0 ) { return { implemtation: '', @@ -1299,6 +1494,7 @@ export const ${operationResponse} = zod.array(${operationResponse}Item)${ : []), ]; }), + ...circularSchemaDefs, ].join('\n\n'), mutators: preprocessResponse ? [preprocessResponse] : [], }; diff --git a/packages/zod/src/zod.test.ts b/packages/zod/src/zod.test.ts index 6fead7a72..6382dbe98 100644 --- a/packages/zod/src/zod.test.ts +++ b/packages/zod/src/zod.test.ts @@ -1908,3 +1908,452 @@ describe('generateZodWithMultiTypeArray', () => { ); }); }); + +describe('circular references', () => { + const context: ContextSpecs = { + output: { + override: { + useDates: false, + }, + }, + } as ContextSpecs; + + it('handles circular references with lazy schema in properties', () => { + // Create a test schema with a circular reference marker + const schemaWithCircularRef = { + __circular__: true, + __refName__: '#/components/schemas/Node', + } as any; + + const result = generateZodValidationSchemaDefinition( + schemaWithCircularRef, + context, + 'testNode', + false, + false, + { required: true }, + ); + + expect(result).toEqual({ + functions: [['circularRef', '#/components/schemas/Node']], + consts: [], + }); + + const parsed = parseZodValidationSchemaDefinition( + result, + context, + false, + false, + false, + ); + + // Should extract Node from the reference + expect(parsed.zod).toBe('Node'); + }); + + it('wraps circular references in object properties with lazy', () => { + const schemaWithCircularRefProperty = { + functions: [ + [ + 'object', + { + id: { + functions: [['number', undefined]], + consts: [], + }, + child: { + functions: [['circularRef', '#/components/schemas/Node']], + consts: [], + }, + }, + ], + ], + consts: [], + }; + + const parsed = parseZodValidationSchemaDefinition( + schemaWithCircularRefProperty, + context, + false, + false, + false, + ); + + expect(parsed.zod).toBe( + 'zod.object({\n "id": zod.number(),\n "child": zod.lazy(() => Node)\n})', + ); + }); + + it('handles circular references in arrays', () => { + const schemaWithCircularRefArray = { + functions: [ + [ + 'object', + { + id: { + functions: [['number', undefined]], + consts: [], + }, + children: { + functions: [['circularRef', '#/components/schemas/Node']], + consts: [], + }, + }, + ], + ], + consts: [], + }; + + const parsed = parseZodValidationSchemaDefinition( + schemaWithCircularRefArray, + context, + false, + false, + false, + ); + + expect(parsed.zod).toBe( + 'zod.object({\n "id": zod.number(),\n "children": zod.lazy(() => Node)\n})', + ); + }); + + it('handles circular references in arrays with optional', () => { + const schemaWithCircularRefArray = { + functions: [ + [ + 'object', + { + id: { + functions: [['number', undefined]], + consts: [], + }, + tags: { + functions: [ + ['circularRef', '#/components/schemas/Tag'], + ['optional', undefined], + ], + consts: [], + }, + }, + ], + ], + consts: [], + }; + + const parsed = parseZodValidationSchemaDefinition( + schemaWithCircularRefArray, + context, + false, + false, + false, + ); + + expect(parsed.zod).toBe( + 'zod.object({\n "id": zod.number(),\n "tags": zod.lazy(() => Tag).optional()\n})', + ); + }); + + it('handles multiple circular references in one object', () => { + const schemaWithMultipleCircularRefs = { + functions: [ + [ + 'object', + { + id: { + functions: [['number', undefined]], + consts: [], + }, + parent: { + functions: [['circularRef', '#/components/schemas/Node']], + consts: [], + }, + sibling: { + functions: [['circularRef', '#/components/schemas/Node']], + consts: [], + }, + child: { + functions: [['circularRef', '#/components/schemas/Node']], + consts: [], + }, + }, + ], + ], + consts: [], + }; + + const parsed = parseZodValidationSchemaDefinition( + schemaWithMultipleCircularRefs, + context, + false, + false, + false, + ); + + expect(parsed.zod).toBe( + 'zod.object({\n "id": zod.number(),\n "parent": zod.lazy(() => Node),\n "sibling": zod.lazy(() => Node),\n "child": zod.lazy(() => Node)\n})', + ); + }); + + it('handles circular references with nullable', () => { + const schemaWithCircularRefNullable = { + functions: [ + [ + 'object', + { + id: { + functions: [['number', undefined]], + consts: [], + }, + next: { + functions: [ + ['circularRef', '#/components/schemas/Node'], + ['nullable', undefined], + ], + consts: [], + }, + }, + ], + ], + consts: [], + }; + + const parsed = parseZodValidationSchemaDefinition( + schemaWithCircularRefNullable, + context, + false, + false, + false, + ); + + expect(parsed.zod).toBe( + 'zod.object({\n "id": zod.number(),\n "next": zod.lazy(() => Node).nullable()\n})', + ); + }); + + it('handles circular references with both optional and nullable', () => { + const schemaWithCircularRefOptionalNullable = { + functions: [ + [ + 'object', + { + id: { + functions: [['number', undefined]], + consts: [], + }, + next: { + functions: [ + ['circularRef', '#/components/schemas/Node'], + ['optional', undefined], + ['nullable', undefined], + ], + consts: [], + }, + }, + ], + ], + consts: [], + }; + + const parsed = parseZodValidationSchemaDefinition( + schemaWithCircularRefOptionalNullable, + context, + false, + false, + false, + ); + + expect(parsed.zod).toBe( + 'zod.object({\n "id": zod.number(),\n "next": zod.lazy(() => Node).optional().nullable()\n})', + ); + }); + + it('handles deeply nested circular references', () => { + const schemaWithNestedCircularRef = { + functions: [ + [ + 'object', + { + id: { + functions: [['number', undefined]], + consts: [], + }, + data: { + functions: [ + [ + 'object', + { + name: { + functions: [['string', undefined]], + consts: [], + }, + parent: { + functions: [['circularRef', '#/components/schemas/Node']], + consts: [], + }, + }, + ], + ], + consts: [], + }, + }, + ], + ], + consts: [], + }; + + const parsed = parseZodValidationSchemaDefinition( + schemaWithNestedCircularRef, + context, + false, + false, + false, + ); + + expect(parsed.zod).toBe( + 'zod.object({\n "id": zod.number(),\n "data": zod.object({\n "name": zod.string(),\n "parent": zod.lazy(() => Node)\n})\n})', + ); + }); + + it('handles circular references in allOf combinations', () => { + const schemaWithCircularRefAllOf = { + functions: [ + [ + 'allOf', + [ + { + functions: [ + [ + 'object', + { + id: { + functions: [['number', undefined]], + consts: [], + }, + }, + ], + ], + consts: [], + }, + { + functions: [ + [ + 'object', + { + child: { + functions: [['circularRef', '#/components/schemas/Node']], + consts: [], + }, + }, + ], + ], + consts: [], + }, + ], + ], + ], + consts: [], + }; + + const parsed = parseZodValidationSchemaDefinition( + schemaWithCircularRefAllOf, + context, + false, + false, + false, + ); + + expect(parsed.zod).toContain('zod.lazy(() => Node)'); + }); + + it('handles circular references with description', () => { + const schemaWithCircularRefAndDescription = { + functions: [ + [ + 'object', + { + id: { + functions: [['number', undefined]], + consts: [], + }, + child: { + functions: [ + ['circularRef', '#/components/schemas/Node'], + ['describe', "'The child node'"], + ], + consts: [], + }, + }, + ], + ], + consts: [], + }; + + const parsed = parseZodValidationSchemaDefinition( + schemaWithCircularRefAndDescription, + context, + false, + false, + false, + ); + + expect(parsed.zod).toBe( + 'zod.object({\n "id": zod.number(),\n "child": zod.lazy(() => Node).describe(\'The child node\')\n})', + ); + }); + + it('handles circular references in arrays correctly', () => { + const schemaWithArrayCircularRef = { + functions: [ + [ + 'array', + { + functions: [['circularRef', '#/components/schemas/Node']], + consts: [], + }, + ], + ], + consts: [], + }; + + const parsed = parseZodValidationSchemaDefinition( + schemaWithArrayCircularRef, + context, + false, + false, + false, + ); + + expect(parsed.zod).toBe('zod.array(zod.lazy(() => Node))'); + }); + + it('handles circular references in arrays with modifiers', () => { + const schemaWithArrayCircularRefModifiers = { + functions: [ + [ + 'array', + { + functions: [ + ['circularRef', '#/components/schemas/Node'], + ['optional', undefined], + ['nullable', undefined], + ], + consts: [], + }, + ], + ], + consts: [], + }; + + const parsed = parseZodValidationSchemaDefinition( + schemaWithArrayCircularRefModifiers, + context, + false, + false, + false, + ); + + expect(parsed.zod).toBe( + 'zod.array(zod.lazy(() => Node).optional().nullable())', + ); + }); +});