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

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #3554 from hey-api/feat/zod-resolvers

feat: add more resolvers to zod

authored by

Lubos and committed by
GitHub
f9b5e001 66e8ae03

+1731 -402
+5
.changeset/two-worms-thank.md
··· 1 + --- 2 + "@hey-api/openapi-ts": patch 3 + --- 4 + 5 + **plugin(zod)**: provide more resolvers
+11 -7
packages/openapi-python/src/plugins/pydantic/v2/walker.ts
··· 60 60 }; 61 61 }, 62 62 array(schema, ctx, walk) { 63 - const applyModifiers = (result: PydanticResult, opts?: { optional?: boolean }) => 63 + const applyModifiers: Parameters<typeof arrayToType>[0]['applyModifiers'] = (result, opts) => 64 64 this.applyModifiers(result, ctx, opts) as PydanticFinal; 65 65 66 66 const { childResults, fieldConstraints, type } = arrayToType({ ··· 111 111 } 112 112 }, 113 113 intersection(items, schemas, parentSchema, ctx) { 114 - const applyModifiers = (result: PydanticResult, opts?: { optional?: boolean }) => 115 - this.applyModifiers(result, ctx, opts) as PydanticFinal; 114 + const applyModifiers: Parameters<typeof intersectionToType>[0]['applyModifiers'] = ( 115 + result, 116 + opts, 117 + ) => this.applyModifiers(result, ctx, opts) as PydanticFinal; 116 118 117 119 const result = intersectionToType({ 118 120 applyModifiers, ··· 156 158 }; 157 159 }, 158 160 object(schema, ctx, walk) { 159 - const applyModifiers = (result: PydanticResult, opts?: { optional?: boolean }) => 160 - this.applyModifiers(result, ctx, opts) as PydanticFinal; 161 + const applyModifiers: Parameters<typeof objectToFields>[0]['applyModifiers'] = ( 162 + result, 163 + opts, 164 + ) => this.applyModifiers(result, ctx, opts) as PydanticFinal; 161 165 162 166 const { childResults, fields, type } = objectToFields({ 163 167 applyModifiers, ··· 203 207 }; 204 208 }, 205 209 tuple(schema, ctx, walk) { 206 - const applyModifiers = (result: PydanticResult, opts?: { optional?: boolean }) => 210 + const applyModifiers: Parameters<typeof tupleToType>[0]['applyModifiers'] = (result, opts) => 207 211 this.applyModifiers(result, ctx, opts) as PydanticFinal; 208 212 209 213 const { childResults, fieldConstraints, type } = tupleToType({ ··· 232 236 }; 233 237 }, 234 238 union(items, schemas, parentSchema, ctx) { 235 - const applyModifiers = (result: PydanticResult, opts?: { optional?: boolean }) => 239 + const applyModifiers: Parameters<typeof unionToType>[0]['applyModifiers'] = (result, opts) => 236 240 this.applyModifiers(result, ctx, opts) as PydanticFinal; 237 241 238 242 const result = unionToType({
+10 -10
packages/openapi-ts/src/plugins/valibot/resolvers.ts
··· 363 363 schema: SchemaWithType<'unknown'>; 364 364 } 365 365 366 - export interface VoidResolverContext extends BaseContext { 367 - /** 368 - * Nodes used to build different parts of the result. 369 - */ 370 - nodes: { 371 - base: (ctx: VoidResolverContext) => PipeResult; 372 - }; 373 - schema: SchemaWithType<'void'>; 374 - } 375 - 376 366 export interface ValidatorResolverContext extends BaseContext { 377 367 operation: IR.OperationObject; 378 368 /** ··· 382 372 schema: Symbol; 383 373 }; 384 374 } 375 + 376 + export interface VoidResolverContext extends BaseContext { 377 + /** 378 + * Nodes used to build different parts of the result. 379 + */ 380 + nodes: { 381 + base: (ctx: VoidResolverContext) => PipeResult; 382 + }; 383 + schema: SchemaWithType<'void'>; 384 + }
+3 -3
packages/openapi-ts/src/plugins/valibot/v1/toAst/union.ts
··· 15 15 const nonNullItems: Array<ValibotResult> = []; 16 16 childResults.forEach((item, index) => { 17 17 const schema = schemas[index]!; 18 - if (schema.type !== 'null') { 18 + if (schema.type !== 'null' && schema.const !== null) { 19 19 nonNullItems.push(item); 20 20 } 21 21 }); ··· 49 49 plugin: ValibotPlugin['Instance']; 50 50 schemas: ReadonlyArray<IR.SchemaObject>; 51 51 }): CompositeHandlerResult { 52 - const { childResults, parentSchema, plugin, schemas } = ctx; 52 + const { applyModifiers, childResults, parentSchema, plugin, schemas } = ctx; 53 53 54 54 const resolverCtx: UnionResolverContext = { 55 55 $, 56 - applyModifiers: ctx.applyModifiers, 56 + applyModifiers, 57 57 childResults, 58 58 nodes: { 59 59 base: baseNode,
+11 -7
packages/openapi-ts/src/plugins/valibot/v1/walker.ts
··· 86 86 }; 87 87 }, 88 88 array(schema, ctx, walk) { 89 - const applyModifiers = (result: ValibotResult, opts?: { optional?: boolean }) => 89 + const applyModifiers: Parameters<typeof arrayToPipes>[0]['applyModifiers'] = (result, opts) => 90 90 this.applyModifiers(result, ctx, opts) as ValibotFinal; 91 91 92 92 const { childResults, pipes } = arrayToPipes({ ··· 145 145 } 146 146 }, 147 147 intersection(items, schemas, parentSchema, ctx) { 148 - const applyModifiers = (result: ValibotResult, opts?: { optional?: boolean }) => 149 - this.applyModifiers(result, ctx, opts) as ValibotFinal; 148 + const applyModifiers: Parameters<typeof intersectionToPipes>[0]['applyModifiers'] = ( 149 + result, 150 + opts, 151 + ) => this.applyModifiers(result, ctx, opts) as ValibotFinal; 150 152 151 153 const { pipes } = intersectionToPipes({ 152 154 applyModifiers, ··· 190 192 }; 191 193 }, 192 194 object(schema, ctx, walk) { 193 - const applyModifiers = (result: ValibotResult, opts: { optional?: boolean }) => 194 - this.applyModifiers(result, ctx, opts) as ValibotFinal; 195 + const applyModifiers: Parameters<typeof objectToPipes>[0]['applyModifiers'] = ( 196 + result, 197 + opts, 198 + ) => this.applyModifiers(result, ctx, opts) as ValibotFinal; 195 199 196 200 const { childResults, pipes } = objectToPipes({ 197 201 applyModifiers, ··· 267 271 }; 268 272 }, 269 273 tuple(schema, ctx, walk) { 270 - const applyModifiers = (result: ValibotResult, opts?: { optional?: boolean }) => 274 + const applyModifiers: Parameters<typeof tupleToPipes>[0]['applyModifiers'] = (result, opts) => 271 275 this.applyModifiers(result, ctx, opts) as ValibotFinal; 272 276 273 277 const { childResults, pipes } = tupleToPipes({ ··· 295 299 }; 296 300 }, 297 301 union(items, schemas, parentSchema, ctx) { 298 - const applyModifiers = (result: ValibotResult, opts?: { optional?: boolean }) => 302 + const applyModifiers: Parameters<typeof unionToPipes>[0]['applyModifiers'] = (result, opts) => 299 303 this.applyModifiers(result, ctx, opts) as ValibotFinal; 300 304 301 305 const hasNull = schemas.some((s) => s.type === 'null') || items.some((i) => i.meta.nullable);
+137 -66
packages/openapi-ts/src/plugins/zod/mini/toAst/array.ts
··· 1 - import type { SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared'; 2 1 import { childContext, deduplicateSchema } from '@hey-api/shared'; 3 2 4 3 import { $ } from '../../../../ts-dsl'; 5 4 import { identifiers } from '../../constants'; 6 - import type { Chain } from '../../shared/chain'; 7 - import type { CompositeHandlerResult, ZodFinal, ZodResult } from '../../shared/types'; 8 - import type { ZodPlugin } from '../../types'; 5 + import type { ArrayResolverContext } from '../../resolvers'; 6 + import type { Chain, ChainResult } from '../../shared/chain'; 7 + import type { CompositeHandlerResult, ZodResult } from '../../shared/types'; 9 8 import { unknownToAst } from './unknown'; 10 9 10 + type ArrayToAstOptions = Pick< 11 + ArrayResolverContext, 12 + 'applyModifiers' | 'plugin' | 'schema' | 'walk' | 'walkerCtx' 13 + >; 14 + 15 + function baseNode(ctx: ArrayResolverContext): Chain { 16 + const { applyModifiers, childResults, plugin, schema, symbols } = ctx; 17 + const { z } = symbols; 18 + 19 + const arrayFn = $(z).attr(identifiers.array); 20 + 21 + let normalizedSchema = schema; 22 + if (normalizedSchema.items) { 23 + normalizedSchema = deduplicateSchema({ schema: normalizedSchema }); 24 + } 25 + 26 + if (!normalizedSchema.items) { 27 + return arrayFn.call( 28 + unknownToAst({ 29 + plugin, 30 + schema: { 31 + type: 'unknown', 32 + }, 33 + }), 34 + ); 35 + } 36 + 37 + if (childResults.length === 1) { 38 + const itemNode = applyModifiers(childResults[0]!, { optional: false }).expression; 39 + return arrayFn.call(itemNode); 40 + } 41 + 42 + if (childResults.length > 1) { 43 + const itemExpressions: Array<Chain> = childResults.map( 44 + (result) => applyModifiers(result, { optional: false }).expression, 45 + ); 46 + 47 + if (normalizedSchema.logicalOperator === 'and') { 48 + return arrayFn.call( 49 + $(z) 50 + .attr(identifiers.intersection) 51 + .call(...itemExpressions), 52 + ); 53 + } else { 54 + return arrayFn.call( 55 + $(z) 56 + .attr(identifiers.union) 57 + .call($.array(...itemExpressions)), 58 + ); 59 + } 60 + } 61 + 62 + return arrayFn.call( 63 + unknownToAst({ 64 + plugin, 65 + schema: { 66 + type: 'unknown', 67 + }, 68 + }), 69 + ); 70 + } 71 + 72 + function lengthNode(ctx: ArrayResolverContext): ChainResult { 73 + const { schema } = ctx; 74 + if (schema.minItems === schema.maxItems && schema.minItems !== undefined) { 75 + return ctx.chain.current.attr(identifiers.length).call($.fromValue(schema.minItems)); 76 + } 77 + } 78 + 79 + function maxLengthNode(ctx: ArrayResolverContext): ChainResult { 80 + const { schema, symbols } = ctx; 81 + const { z } = symbols; 82 + if (schema.maxItems === undefined) return; 83 + return $(z).attr(identifiers.maxLength).call($.fromValue(schema.maxItems)); 84 + } 85 + 86 + function minLengthNode(ctx: ArrayResolverContext): ChainResult { 87 + const { schema, symbols } = ctx; 88 + const { z } = symbols; 89 + if (schema.minItems === undefined) return; 90 + return $(z).attr(identifiers.minLength).call($.fromValue(schema.minItems)); 91 + } 92 + 93 + function arrayResolver(ctx: ArrayResolverContext): Chain { 94 + const baseResult = ctx.nodes.base(ctx); 95 + ctx.chain.current = baseResult; 96 + 97 + const lengthResult = ctx.nodes.length(ctx); 98 + if (lengthResult) { 99 + ctx.chain.current = lengthResult; 100 + } else { 101 + const minLengthResult = ctx.nodes.minLength(ctx); 102 + const maxLengthResult = ctx.nodes.maxLength(ctx); 103 + 104 + if (minLengthResult && maxLengthResult) { 105 + ctx.chain.current = ctx.chain.current 106 + .attr(identifiers.check) 107 + .call(minLengthResult, maxLengthResult); 108 + } else if (minLengthResult) { 109 + ctx.chain.current = ctx.chain.current.attr(identifiers.check).call(minLengthResult); 110 + } else if (maxLengthResult) { 111 + ctx.chain.current = ctx.chain.current.attr(identifiers.check).call(maxLengthResult); 112 + } 113 + } 114 + 115 + return ctx.chain.current; 116 + } 117 + 11 118 export function arrayToAst({ 12 119 applyModifiers, 13 120 plugin, 14 121 schema, 15 122 walk, 16 123 walkerCtx, 17 - }: { 18 - applyModifiers: (result: ZodResult, opts: { optional?: boolean }) => ZodFinal; 19 - plugin: ZodPlugin['Instance']; 20 - schema: SchemaWithType<'array'>; 21 - walk: Walker<ZodResult, ZodPlugin['Instance']>; 22 - walkerCtx: SchemaVisitorContext<ZodPlugin['Instance']>; 23 - }): CompositeHandlerResult { 124 + }: ArrayToAstOptions): CompositeHandlerResult { 24 125 const childResults: Array<ZodResult> = []; 25 126 let schemaCopy = schema; 26 127 27 128 const z = plugin.external('zod.z'); 28 - const functionName = $(z).attr(identifiers.array); 29 129 30 - let arrayExpression: ReturnType<typeof $.call> | undefined; 31 - 32 - if (!schemaCopy.items) { 33 - arrayExpression = functionName.call( 34 - unknownToAst({ 35 - plugin, 36 - schema: { type: 'unknown' }, 37 - }), 38 - ); 39 - } else { 130 + if (schemaCopy.items) { 40 131 schemaCopy = deduplicateSchema({ schema: schemaCopy }); 41 132 42 - const itemExpressions: Array<Chain> = []; 43 - 44 133 schemaCopy.items!.forEach((item, index) => { 45 134 const itemResult = walk(item, childContext(walkerCtx, 'items', index)); 46 135 childResults.push(itemResult); 47 - 48 - const finalExpr = applyModifiers(itemResult, { optional: false }); 49 - itemExpressions.push(finalExpr.expression); 50 136 }); 51 - 52 - if (itemExpressions.length === 1) { 53 - arrayExpression = functionName.call(...itemExpressions); 54 - } else { 55 - if (schemaCopy.logicalOperator === 'and') { 56 - arrayExpression = functionName.call( 57 - $(z) 58 - .attr(identifiers.intersection) 59 - .call(...itemExpressions), 60 - ); 61 - } else { 62 - arrayExpression = $(z) 63 - .attr(identifiers.array) 64 - .call( 65 - $(z) 66 - .attr(identifiers.union) 67 - .call($.array(...itemExpressions)), 68 - ); 69 - } 70 - } 71 137 } 72 138 73 - if (schemaCopy.minItems === schemaCopy.maxItems && schemaCopy.minItems !== undefined) { 74 - arrayExpression = arrayExpression 75 - .attr(identifiers.length) 76 - .call($.fromValue(schemaCopy.minItems)); 77 - } else { 78 - const checks: Array<ReturnType<typeof $.call>> = []; 79 - 80 - if (schemaCopy.minItems !== undefined) { 81 - checks.push($(z).attr(identifiers.minLength).call($.fromValue(schemaCopy.minItems))); 82 - } 83 - 84 - if (schemaCopy.maxItems !== undefined) { 85 - checks.push($(z).attr(identifiers.maxLength).call($.fromValue(schemaCopy.maxItems))); 86 - } 139 + const ctx: ArrayResolverContext = { 140 + $, 141 + applyModifiers, 142 + chain: { 143 + current: $(z), 144 + }, 145 + childResults, 146 + nodes: { 147 + base: baseNode, 148 + length: lengthNode, 149 + maxLength: maxLengthNode, 150 + minLength: minLengthNode, 151 + }, 152 + plugin, 153 + schema, 154 + symbols: { 155 + z, 156 + }, 157 + walk, 158 + walkerCtx, 159 + }; 87 160 88 - if (checks.length) { 89 - arrayExpression = arrayExpression.attr(identifiers.check).call(...checks); 90 - } 91 - } 161 + const resolver = plugin.config['~resolvers']?.array; 162 + const expression = resolver?.(ctx) ?? arrayResolver(ctx); 92 163 93 164 return { 94 165 childResults, 95 - expression: arrayExpression, 166 + expression, 96 167 }; 97 168 }
+45 -6
packages/openapi-ts/src/plugins/zod/mini/toAst/boolean.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 - import type { Chain } from '../../shared/chain'; 5 + import type { BooleanResolverContext } from '../../resolvers'; 6 + import type { Chain, ChainResult } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: BooleanResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.boolean).call(); 13 + } 14 + 15 + function constNode(ctx: BooleanResolverContext): ChainResult { 16 + const { schema, symbols } = ctx; 17 + const { z } = symbols; 18 + if (typeof schema.const !== 'boolean') return; 19 + return $(z).attr(identifiers.literal).call($.literal(schema.const)); 20 + } 21 + 22 + function booleanResolver(ctx: BooleanResolverContext): Chain { 23 + const constResult = ctx.nodes.const(ctx); 24 + if (constResult) { 25 + ctx.chain.current = constResult; 26 + return ctx.chain.current; 27 + } 28 + 29 + const baseResult = ctx.nodes.base(ctx); 30 + ctx.chain.current = baseResult; 31 + 32 + return ctx.chain.current; 33 + } 34 + 8 35 export function booleanToAst({ 9 36 plugin, 10 37 schema, ··· 13 40 schema: SchemaWithType<'boolean'>; 14 41 }): Chain { 15 42 const z = plugin.external('zod.z'); 16 - 17 - if (typeof schema.const === 'boolean') { 18 - return $(z).attr(identifiers.literal).call($.literal(schema.const)); 19 - } 43 + const ctx: BooleanResolverContext = { 44 + $, 45 + chain: { 46 + current: $(z), 47 + }, 48 + nodes: { 49 + base: baseNode, 50 + const: constNode, 51 + }, 52 + plugin, 53 + schema, 54 + symbols: { 55 + z, 56 + }, 57 + }; 20 58 21 - return $(z).attr(identifiers.boolean).call(); 59 + const resolver = plugin.config['~resolvers']?.boolean; 60 + return resolver?.(ctx) ?? booleanResolver(ctx); 22 61 }
+31 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/never.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 + import type { NeverResolverContext } from '../../resolvers'; 5 6 import type { Chain } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: NeverResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.never).call(); 13 + } 14 + 15 + function neverResolver(ctx: NeverResolverContext): Chain { 16 + const baseResult = ctx.nodes.base(ctx); 17 + ctx.chain.current = baseResult; 18 + return ctx.chain.current; 19 + } 20 + 8 21 export function neverToAst({ 9 22 plugin, 23 + schema, 10 24 }: { 11 25 plugin: ZodPlugin['Instance']; 12 26 schema: SchemaWithType<'never'>; 13 27 }): Chain { 14 28 const z = plugin.external('zod.z'); 15 - return $(z).attr(identifiers.never).call(); 29 + const ctx: NeverResolverContext = { 30 + $, 31 + chain: { 32 + current: $(z), 33 + }, 34 + nodes: { 35 + base: baseNode, 36 + }, 37 + plugin, 38 + schema, 39 + symbols: { 40 + z, 41 + }, 42 + }; 43 + 44 + const resolver = plugin.config['~resolvers']?.never; 45 + return resolver?.(ctx) ?? neverResolver(ctx); 16 46 }
+31 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/null.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 + import type { NullResolverContext } from '../../resolvers'; 5 6 import type { Chain } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: NullResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.null).call(); 13 + } 14 + 15 + function nullResolver(ctx: NullResolverContext): Chain { 16 + const baseResult = ctx.nodes.base(ctx); 17 + ctx.chain.current = baseResult; 18 + return ctx.chain.current; 19 + } 20 + 8 21 export function nullToAst({ 9 22 plugin, 23 + schema, 10 24 }: { 11 25 plugin: ZodPlugin['Instance']; 12 26 schema: SchemaWithType<'null'>; 13 27 }): Chain { 14 28 const z = plugin.external('zod.z'); 15 - return $(z).attr(identifiers.null).call(); 29 + const ctx: NullResolverContext = { 30 + $, 31 + chain: { 32 + current: $(z), 33 + }, 34 + nodes: { 35 + base: baseNode, 36 + }, 37 + plugin, 38 + schema, 39 + symbols: { 40 + z, 41 + }, 42 + }; 43 + 44 + const resolver = plugin.config['~resolvers']?.null; 45 + return resolver?.(ctx) ?? nullResolver(ctx); 16 46 }
+3 -4
packages/openapi-ts/src/plugins/zod/mini/toAst/object.ts
··· 84 84 85 85 export function objectToAst(options: ObjectToAstOptions): CompositeHandlerResult { 86 86 const { applyModifiers, plugin, schema, walk, walkerCtx } = options; 87 - 88 87 const childResults: Array<ZodResult> = []; 89 - 88 + const z = plugin.external('zod.z'); 90 89 const ctx: ExtendedContext = { 91 90 $, 92 91 _childResults: childResults, 93 92 applyModifiers, 94 93 chain: { 95 - current: $(plugin.external('zod.z')), 94 + current: $(z), 96 95 }, 97 96 nodes: { 98 97 additionalProperties: additionalPropertiesNode, ··· 102 101 plugin, 103 102 schema, 104 103 symbols: { 105 - z: plugin.external('zod.z'), 104 + z, 106 105 }, 107 106 walk, 108 107 walkerCtx,
+78 -31
packages/openapi-ts/src/plugins/zod/mini/toAst/tuple.ts
··· 1 - import type { SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared'; 2 1 import { childContext } from '@hey-api/shared'; 3 2 4 3 import { $ } from '../../../../ts-dsl'; 5 4 import { identifiers } from '../../constants'; 6 - import type { Chain } from '../../shared/chain'; 7 - import type { CompositeHandlerResult, ZodFinal, ZodResult } from '../../shared/types'; 8 - import type { ZodPlugin } from '../../types'; 5 + import type { TupleResolverContext } from '../../resolvers'; 6 + import type { Chain, ChainResult } from '../../shared/chain'; 7 + import type { CompositeHandlerResult, ZodResult } from '../../shared/types'; 8 + 9 + type TupleToAstOptions = Pick< 10 + TupleResolverContext, 11 + 'applyModifiers' | 'plugin' | 'schema' | 'walk' | 'walkerCtx' 12 + >; 13 + 14 + function baseNode(ctx: TupleResolverContext): Chain { 15 + const { applyModifiers, childResults, symbols } = ctx; 16 + const { z } = symbols; 17 + 18 + const tupleFn = $(z).attr(identifiers.tuple); 19 + 20 + if (childResults.length === 0) { 21 + return tupleFn.call($.array()); 22 + } 23 + 24 + const tupleElements = childResults.map( 25 + (result) => applyModifiers(result, { optional: false }).expression, 26 + ); 27 + 28 + return tupleFn.call($.array(...tupleElements)); 29 + } 30 + 31 + function constNode(ctx: TupleResolverContext): ChainResult { 32 + const { schema, symbols } = ctx; 33 + const { z } = symbols; 34 + 35 + if (!schema.const || !Array.isArray(schema.const)) return; 36 + 37 + const tupleElements = schema.const.map((value) => 38 + $(z).attr(identifiers.literal).call($.fromValue(value)), 39 + ); 40 + 41 + return $(z) 42 + .attr(identifiers.tuple) 43 + .call($.array(...tupleElements)); 44 + } 45 + 46 + function tupleResolver(ctx: TupleResolverContext): Chain { 47 + const constResult = ctx.nodes.const(ctx); 48 + if (constResult) { 49 + ctx.chain.current = constResult; 50 + return ctx.chain.current; 51 + } 52 + 53 + const baseResult = ctx.nodes.base(ctx); 54 + ctx.chain.current = baseResult; 55 + 56 + return ctx.chain.current; 57 + } 9 58 10 59 export function tupleToAst({ 11 60 applyModifiers, ··· 13 62 schema, 14 63 walk, 15 64 walkerCtx, 16 - }: { 17 - applyModifiers: (result: ZodResult, opts: { optional?: boolean }) => ZodFinal; 18 - plugin: ZodPlugin['Instance']; 19 - schema: SchemaWithType<'tuple'>; 20 - walk: Walker<ZodResult, ZodPlugin['Instance']>; 21 - walkerCtx: SchemaVisitorContext<ZodPlugin['Instance']>; 22 - }): CompositeHandlerResult { 65 + }: TupleToAstOptions): CompositeHandlerResult { 23 66 const childResults: Array<ZodResult> = []; 24 67 25 68 const z = plugin.external('zod.z'); 26 69 27 - if (schema.const && Array.isArray(schema.const)) { 28 - const tupleElements = schema.const.map((value) => 29 - $(z).attr(identifiers.literal).call($.fromValue(value)), 30 - ); 31 - return { 32 - childResults, 33 - expression: $(z) 34 - .attr(identifiers.tuple) 35 - .call($.array(...tupleElements)), 36 - }; 37 - } 38 - 39 - const tupleElements: Array<Chain> = []; 40 - 41 70 if (schema.items) { 42 71 schema.items.forEach((item, index) => { 43 72 const itemResult = walk(item, childContext(walkerCtx, 'items', index)); 44 73 childResults.push(itemResult); 45 - 46 - const finalExpr = applyModifiers(itemResult, { optional: false }); 47 - tupleElements.push(finalExpr.expression); 48 74 }); 49 75 } 50 76 77 + const ctx: TupleResolverContext = { 78 + $, 79 + applyModifiers, 80 + chain: { 81 + current: $(z), 82 + }, 83 + childResults, 84 + nodes: { 85 + base: baseNode, 86 + const: constNode, 87 + }, 88 + plugin, 89 + schema, 90 + symbols: { 91 + z, 92 + }, 93 + walk, 94 + walkerCtx, 95 + }; 96 + 97 + const resolver = plugin.config['~resolvers']?.tuple; 98 + const expression = resolver?.(ctx) ?? tupleResolver(ctx); 99 + 51 100 return { 52 101 childResults, 53 - expression: $(z) 54 - .attr(identifiers.tuple) 55 - .call($.array(...tupleElements)), 102 + expression, 56 103 }; 57 104 }
+31 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/undefined.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 + import type { UndefinedResolverContext } from '../../resolvers'; 5 6 import type { Chain } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: UndefinedResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.undefined).call(); 13 + } 14 + 15 + function undefinedResolver(ctx: UndefinedResolverContext): Chain { 16 + const baseResult = ctx.nodes.base(ctx); 17 + ctx.chain.current = baseResult; 18 + return ctx.chain.current; 19 + } 20 + 8 21 export function undefinedToAst({ 9 22 plugin, 23 + schema, 10 24 }: { 11 25 plugin: ZodPlugin['Instance']; 12 26 schema: SchemaWithType<'undefined'>; 13 27 }): Chain { 14 28 const z = plugin.external('zod.z'); 15 - return $(z).attr(identifiers.undefined).call(); 29 + const ctx: UndefinedResolverContext = { 30 + $, 31 + chain: { 32 + current: $(z), 33 + }, 34 + nodes: { 35 + base: baseNode, 36 + }, 37 + plugin, 38 + schema, 39 + symbols: { 40 + z, 41 + }, 42 + }; 43 + 44 + const resolver = plugin.config['~resolvers']?.undefined; 45 + return resolver?.(ctx) ?? undefinedResolver(ctx); 16 46 }
+31 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/unknown.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 + import type { UnknownResolverContext } from '../../resolvers'; 5 6 import type { Chain } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: UnknownResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.unknown).call(); 13 + } 14 + 15 + function unknownResolver(ctx: UnknownResolverContext): Chain { 16 + const baseResult = ctx.nodes.base(ctx); 17 + ctx.chain.current = baseResult; 18 + return ctx.chain.current; 19 + } 20 + 8 21 export function unknownToAst({ 9 22 plugin, 23 + schema, 10 24 }: { 11 25 plugin: ZodPlugin['Instance']; 12 26 schema: SchemaWithType<'unknown'>; 13 27 }): Chain { 14 28 const z = plugin.external('zod.z'); 15 - return $(z).attr(identifiers.unknown).call(); 29 + const ctx: UnknownResolverContext = { 30 + $, 31 + chain: { 32 + current: $(z), 33 + }, 34 + nodes: { 35 + base: baseNode, 36 + }, 37 + plugin, 38 + schema, 39 + symbols: { 40 + z, 41 + }, 42 + }; 43 + 44 + const resolver = plugin.config['~resolvers']?.unknown; 45 + return resolver?.(ctx) ?? unknownResolver(ctx); 16 46 }
+31 -1
packages/openapi-ts/src/plugins/zod/mini/toAst/void.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 + import type { VoidResolverContext } from '../../resolvers'; 5 6 import type { Chain } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: VoidResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.void).call(); 13 + } 14 + 15 + function voidResolver(ctx: VoidResolverContext): Chain { 16 + const baseResult = ctx.nodes.base(ctx); 17 + ctx.chain.current = baseResult; 18 + return ctx.chain.current; 19 + } 20 + 8 21 export function voidToAst({ 9 22 plugin, 23 + schema, 10 24 }: { 11 25 plugin: ZodPlugin['Instance']; 12 26 schema: SchemaWithType<'void'>; 13 27 }): Chain { 14 28 const z = plugin.external('zod.z'); 15 - return $(z).attr(identifiers.void).call(); 29 + const ctx: VoidResolverContext = { 30 + $, 31 + chain: { 32 + current: $(z), 33 + }, 34 + nodes: { 35 + base: baseNode, 36 + }, 37 + plugin, 38 + schema, 39 + symbols: { 40 + z, 41 + }, 42 + }; 43 + 44 + const resolver = plugin.config['~resolvers']?.void; 45 + return resolver?.(ctx) ?? voidResolver(ctx); 16 46 }
+4 -4
packages/openapi-ts/src/plugins/zod/mini/walker.ts
··· 71 71 }; 72 72 }, 73 73 array(schema, ctx, walk) { 74 - const applyModifiers = (result: ZodResult, opts: { optional?: boolean }) => 74 + const applyModifiers: Parameters<typeof arrayToAst>[0]['applyModifiers'] = (result, opts) => 75 75 this.applyModifiers(result, ctx, opts) as ZodFinal; 76 76 const { childResults, expression } = arrayToAst({ 77 77 applyModifiers, ··· 193 193 }; 194 194 }, 195 195 object(schema, ctx, walk) { 196 - const applyModifiers = (result: ZodResult, opts: { optional?: boolean }) => 196 + const applyModifiers: Parameters<typeof objectToAst>[0]['applyModifiers'] = (result, opts) => 197 197 this.applyModifiers(result, ctx, opts) as ZodFinal; 198 198 const { childResults, expression } = objectToAst({ 199 199 applyModifiers, ··· 287 287 }; 288 288 }, 289 289 tuple(schema, ctx, walk) { 290 - const applyModifiers = (result: ZodResult, opts: { optional?: boolean }) => 290 + const applyModifiers: Parameters<typeof tupleToAst>[0]['applyModifiers'] = (result, opts) => 291 291 this.applyModifiers(result, ctx, opts) as ZodFinal; 292 292 const { childResults, expression } = tupleToAst({ 293 293 applyModifiers, ··· 323 323 324 324 items.forEach((item, index) => { 325 325 const schema = schemas[index]!; 326 - if (schema.type !== 'null') { 326 + if (schema.type !== 'null' && schema.const !== null) { 327 327 nonNullItems.push(item); 328 328 } 329 329 });
+250
packages/openapi-ts/src/plugins/zod/resolvers.ts
··· 12 12 13 13 export type Resolvers = Plugin.Resolvers<{ 14 14 /** 15 + * Resolver for array schemas. 16 + * 17 + * Allows customization of how array types are rendered. 18 + * 19 + * Returning `undefined` will execute the default resolver logic. 20 + */ 21 + array?: (ctx: ArrayResolverContext) => ChainResult; 22 + /** 23 + * Resolver for boolean schemas. 24 + * 25 + * Allows customization of how boolean types are rendered. 26 + * 27 + * Returning `undefined` will execute the default resolver logic. 28 + */ 29 + boolean?: (ctx: BooleanResolverContext) => ChainResult; 30 + /** 15 31 * Resolver for enum schemas. 16 32 * 17 33 * Allows customization of how enum types are rendered. ··· 20 36 */ 21 37 enum?: (ctx: EnumResolverContext) => ChainResult; 22 38 /** 39 + * Resolver for intersection schemas. 40 + * 41 + * Allows customization of how intersection types are rendered. 42 + * 43 + * Returning `undefined` will execute the default resolver logic. 44 + */ 45 + intersection?: (ctx: IntersectionResolverContext) => ChainResult; 46 + /** 47 + * Resolver for never schemas. 48 + * 49 + * Allows customization of how never types are rendered. 50 + * 51 + * Returning `undefined` will execute the default resolver logic. 52 + */ 53 + never?: (ctx: NeverResolverContext) => ChainResult; 54 + /** 55 + * Resolver for null schemas. 56 + * 57 + * Allows customization of how null types are rendered. 58 + * 59 + * Returning `undefined` will execute the default resolver logic. 60 + */ 61 + null?: (ctx: NullResolverContext) => ChainResult; 62 + /** 23 63 * Resolver for number schemas. 24 64 * 25 65 * Allows customization of how number types are rendered. ··· 44 84 */ 45 85 string?: (ctx: StringResolverContext) => ChainResult; 46 86 /** 87 + * Resolver for tuple schemas. 88 + * 89 + * Allows customization of how tuple types are rendered. 90 + * 91 + * Returning `undefined` will execute the default resolver logic. 92 + */ 93 + tuple?: (ctx: TupleResolverContext) => ChainResult; 94 + /** 95 + * Resolver for undefined schemas. 96 + * 97 + * Allows customization of how undefined types are rendered. 98 + * 99 + * Returning `undefined` will execute the default resolver logic. 100 + */ 101 + undefined?: (ctx: UndefinedResolverContext) => ChainResult; 102 + /** 103 + * Resolver for union schemas. 104 + * 105 + * Allows customization of how union types are rendered. 106 + * 107 + * Returning `undefined` will execute the default resolver logic. 108 + */ 109 + union?: (ctx: UnionResolverContext) => ChainResult; 110 + /** 111 + * Resolver for unknown schemas. 112 + * 113 + * Allows customization of how unknown types are rendered. 114 + * 115 + * Returning `undefined` will execute the default resolver logic. 116 + */ 117 + unknown?: (ctx: UnknownResolverContext) => ChainResult; 118 + /** 47 119 * Resolvers for request and response validators. 48 120 * 49 121 * Allow customization of validator function bodies. ··· 68 140 */ 69 141 response?: ValidatorResolver; 70 142 }; 143 + /** 144 + * Resolver for void schemas. 145 + * 146 + * Allows customization of how void types are rendered. 147 + * 148 + * Returning `undefined` will execute the default resolver logic. 149 + */ 150 + void?: (ctx: VoidResolverContext) => ChainResult; 71 151 }>; 72 152 73 153 type ValidatorResolver = ( ··· 100 180 }; 101 181 } 102 182 183 + export interface ArrayResolverContext extends BaseContext { 184 + applyModifiers: (result: ZodResult, opts?: { optional?: boolean }) => ZodFinal; 185 + /** 186 + * Child results from processing array items. 187 + */ 188 + childResults: Array<ZodResult>; 189 + /** 190 + * Nodes used to build different parts of the result. 191 + */ 192 + nodes: { 193 + /** 194 + * Returns the base array expression (z.array(...)). 195 + */ 196 + base: (ctx: ArrayResolverContext) => Chain; 197 + /** 198 + * Returns a length constraint (when minItems === maxItems), if applicable. 199 + */ 200 + length: (ctx: ArrayResolverContext) => ChainResult; 201 + /** 202 + * Returns a maxItems constraint, if applicable. 203 + */ 204 + maxLength: (ctx: ArrayResolverContext) => ChainResult; 205 + /** 206 + * Returns a minItems constraint, if applicable. 207 + */ 208 + minLength: (ctx: ArrayResolverContext) => ChainResult; 209 + }; 210 + schema: SchemaWithType<'array'>; 211 + walk: Walker<ZodResult, ZodPlugin['Instance']>; 212 + walkerCtx: SchemaVisitorContext<ZodPlugin['Instance']>; 213 + } 214 + 215 + export interface BooleanResolverContext extends BaseContext { 216 + /** 217 + * Nodes used to build different parts of the result. 218 + */ 219 + nodes: { 220 + /** 221 + * Returns the base boolean expression (z.boolean()). 222 + */ 223 + base: (ctx: BooleanResolverContext) => Chain; 224 + /** 225 + * Returns a literal expression for the const value, if present. 226 + */ 227 + const: (ctx: BooleanResolverContext) => ChainResult; 228 + }; 229 + schema: SchemaWithType<'boolean'>; 230 + } 231 + 103 232 export interface EnumResolverContext extends BaseContext { 104 233 /** 105 234 * Nodes used to build different parts of the result. ··· 132 261 }; 133 262 }; 134 263 schema: SchemaWithType<'enum'>; 264 + } 265 + 266 + export interface IntersectionResolverContext extends BaseContext { 267 + childResults: Array<ZodResult>; 268 + /** 269 + * Nodes used to build different parts of the result. 270 + */ 271 + nodes: { 272 + /** 273 + * Returns the base intersection expression. 274 + */ 275 + base: (ctx: IntersectionResolverContext) => Chain; 276 + }; 277 + parentSchema: IR.SchemaObject; 278 + schema: IR.SchemaObject; 279 + walk: Walker<ZodResult, ZodPlugin['Instance']>; 280 + walkerCtx: SchemaVisitorContext<ZodPlugin['Instance']>; 281 + } 282 + 283 + export interface NeverResolverContext extends BaseContext { 284 + /** 285 + * Nodes used to build different parts of the result. 286 + */ 287 + nodes: { 288 + /** 289 + * Returns the base never expression (z.never()). 290 + */ 291 + base: (ctx: NeverResolverContext) => Chain; 292 + }; 293 + schema: SchemaWithType<'never'>; 294 + } 295 + 296 + export interface NullResolverContext extends BaseContext { 297 + /** 298 + * Nodes used to build different parts of the result. 299 + */ 300 + nodes: { 301 + /** 302 + * Returns the base null expression (z.null()). 303 + */ 304 + base: (ctx: NullResolverContext) => Chain; 305 + }; 306 + schema: SchemaWithType<'null'>; 135 307 } 136 308 137 309 export interface NumberResolverContext extends BaseContext { ··· 233 405 schema: SchemaWithType<'string'>; 234 406 } 235 407 408 + export interface TupleResolverContext extends BaseContext { 409 + applyModifiers: (result: ZodResult, opts?: { optional?: boolean }) => ZodFinal; 410 + childResults: Array<ZodResult>; 411 + /** 412 + * Nodes used to build different parts of the result. 413 + */ 414 + nodes: { 415 + /** 416 + * Returns the base tuple expression (z.tuple([...])). 417 + */ 418 + base: (ctx: TupleResolverContext) => Chain; 419 + /** 420 + * Returns a literal expression for the const value, if present. 421 + */ 422 + const: (ctx: TupleResolverContext) => ChainResult; 423 + }; 424 + schema: SchemaWithType<'tuple'>; 425 + walk: Walker<ZodResult, ZodPlugin['Instance']>; 426 + walkerCtx: SchemaVisitorContext<ZodPlugin['Instance']>; 427 + } 428 + 429 + export interface UndefinedResolverContext extends BaseContext { 430 + /** 431 + * Nodes used to build different parts of the result. 432 + */ 433 + nodes: { 434 + /** 435 + * Returns the base undefined expression (z.undefined()). 436 + */ 437 + base: (ctx: UndefinedResolverContext) => Chain; 438 + }; 439 + schema: SchemaWithType<'undefined'>; 440 + } 441 + 442 + export interface UnionResolverContext extends BaseContext { 443 + childResults: Array<ZodResult>; 444 + /** 445 + * Nodes used to build different parts of the result. 446 + */ 447 + nodes: { 448 + /** 449 + * Returns the base union expression. 450 + */ 451 + base: (ctx: UnionResolverContext) => Chain; 452 + }; 453 + parentSchema: IR.SchemaObject; 454 + schema: IR.SchemaObject; 455 + schemas: ReadonlyArray<IR.SchemaObject>; 456 + walk: Walker<ZodResult, ZodPlugin['Instance']>; 457 + walkerCtx: SchemaVisitorContext<ZodPlugin['Instance']>; 458 + } 459 + 460 + export interface UnknownResolverContext extends BaseContext { 461 + /** 462 + * Nodes used to build different parts of the result. 463 + */ 464 + nodes: { 465 + /** 466 + * Returns the base unknown expression (z.unknown()). 467 + */ 468 + base: (ctx: UnknownResolverContext) => Chain; 469 + }; 470 + schema: SchemaWithType<'unknown'>; 471 + } 472 + 236 473 export interface ValidatorResolverContext extends BaseContext { 237 474 operation: IR.OperationObject; 238 475 /** ··· 242 479 schema: Symbol; 243 480 }; 244 481 } 482 + 483 + export interface VoidResolverContext extends BaseContext { 484 + /** 485 + * Nodes used to build different parts of the result. 486 + */ 487 + nodes: { 488 + /** 489 + * Returns the base void expression (z.void()). 490 + */ 491 + base: (ctx: VoidResolverContext) => Chain; 492 + }; 493 + schema: SchemaWithType<'void'>; 494 + }
+142 -80
packages/openapi-ts/src/plugins/zod/v3/toAst/array.ts
··· 1 - import type { SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared'; 2 1 import { childContext, deduplicateSchema } from '@hey-api/shared'; 3 2 4 3 import { $ } from '../../../../ts-dsl'; 5 4 import { identifiers } from '../../constants'; 6 - import type { Chain } from '../../shared/chain'; 7 - import type { CompositeHandlerResult, ZodFinal, ZodResult } from '../../shared/types'; 8 - import type { ZodPlugin } from '../../types'; 5 + import type { ArrayResolverContext } from '../../resolvers'; 6 + import type { Chain, ChainResult } from '../../shared/chain'; 7 + import type { CompositeHandlerResult, ZodResult } from '../../shared/types'; 9 8 import { unknownToAst } from './unknown'; 10 9 11 - interface ArrayToAstOptions { 12 - applyModifiers: (result: ZodResult, opts: { optional?: boolean }) => ZodFinal; 13 - plugin: ZodPlugin['Instance']; 14 - schema: SchemaWithType<'array'>; 15 - walk: Walker<ZodResult, ZodPlugin['Instance']>; 16 - walkerCtx: SchemaVisitorContext<ZodPlugin['Instance']>; 17 - } 10 + type ArrayToAstOptions = Pick< 11 + ArrayResolverContext, 12 + 'applyModifiers' | 'plugin' | 'schema' | 'walk' | 'walkerCtx' 13 + >; 18 14 19 - type AstExpression = Chain; 20 - 21 - export function arrayToAst({ 22 - applyModifiers, 23 - plugin, 24 - schema, 25 - walk, 26 - walkerCtx, 27 - }: ArrayToAstOptions): CompositeHandlerResult { 28 - const childResults: Array<ZodResult> = []; 29 - let schemaCopy = schema; 15 + function baseNode(ctx: ArrayResolverContext): Chain { 16 + const { applyModifiers, childResults, plugin, schema, symbols } = ctx; 17 + const { z } = symbols; 30 18 31 - const z = plugin.external('zod.z'); 32 - const functionName = $(z).attr(identifiers.array); 19 + const arrayFn = $(z).attr(identifiers.array); 33 20 34 - let arrayExpression: ReturnType<typeof $.call> | undefined; 21 + let normalizedSchema = schema; 22 + if (normalizedSchema.items) { 23 + normalizedSchema = deduplicateSchema({ schema: normalizedSchema }); 24 + } 35 25 36 - if (!schemaCopy.items) { 37 - arrayExpression = functionName.call( 26 + if (!normalizedSchema.items) { 27 + return arrayFn.call( 38 28 unknownToAst({ 39 29 plugin, 40 30 schema: { ··· 42 32 }, 43 33 }), 44 34 ); 45 - } else { 46 - schemaCopy = deduplicateSchema({ schema: schemaCopy }); 35 + } 47 36 48 - const itemExpressions: Array<Chain> = []; 37 + if (childResults.length === 1) { 38 + const itemNode = applyModifiers(childResults[0]!, { optional: false }).expression; 39 + return arrayFn.call(itemNode); 40 + } 49 41 50 - schemaCopy.items!.forEach((item, index) => { 51 - const itemResult = walk(item, childContext(walkerCtx, 'items', index)); 52 - childResults.push(itemResult); 53 - 54 - const finalExpr = applyModifiers(itemResult, { optional: false }); 55 - itemExpressions.push(finalExpr.expression); 56 - }); 42 + if (childResults.length > 1) { 43 + const itemExpressions: Array<Chain> = childResults.map( 44 + (result) => applyModifiers(result, { optional: false }).expression, 45 + ); 57 46 58 - if (itemExpressions.length === 1) { 59 - arrayExpression = functionName.call(...itemExpressions); 60 - } else { 61 - if (schemaCopy.logicalOperator === 'and') { 62 - const firstSchema = schemaCopy.items![0]!; 63 - let intersectionExpression: AstExpression; 64 - if ( 65 - firstSchema.logicalOperator === 'or' || 66 - (firstSchema.type && firstSchema.type !== 'object') 67 - ) { 68 - intersectionExpression = $(z) 69 - .attr(identifiers.intersection) 70 - .call(...itemExpressions); 71 - } else { 72 - intersectionExpression = itemExpressions[0]!; 73 - for (let i = 1; i < itemExpressions.length; i++) { 74 - intersectionExpression = intersectionExpression 75 - .attr(identifiers.and) 76 - .call(itemExpressions[i]); 77 - } 47 + const firstSchema = normalizedSchema.items[0]; 48 + if (normalizedSchema.logicalOperator === 'and') { 49 + let intersectionExpression: Chain; 50 + if ( 51 + firstSchema?.logicalOperator === 'or' || 52 + (firstSchema?.type && firstSchema.type !== 'object') 53 + ) { 54 + intersectionExpression = $(z) 55 + .attr(identifiers.intersection) 56 + .call(...itemExpressions); 57 + } else { 58 + intersectionExpression = itemExpressions[0]!; 59 + for (let i = 1; i < itemExpressions.length; i++) { 60 + intersectionExpression = intersectionExpression 61 + .attr(identifiers.and) 62 + .call(itemExpressions[i]); 78 63 } 64 + } 79 65 80 - arrayExpression = functionName.call(intersectionExpression); 81 - } else { 82 - arrayExpression = $(z) 83 - .attr(identifiers.array) 84 - .call( 85 - $(z) 86 - .attr(identifiers.union) 87 - .call($.array(...itemExpressions)), 88 - ); 89 - } 66 + return arrayFn.call(intersectionExpression); 67 + } else { 68 + return arrayFn.call( 69 + $(z) 70 + .attr(identifiers.union) 71 + .call($.array(...itemExpressions)), 72 + ); 90 73 } 91 74 } 92 75 93 - if (schemaCopy.minItems === schemaCopy.maxItems && schemaCopy.minItems !== undefined) { 94 - arrayExpression = arrayExpression 95 - .attr(identifiers.length) 96 - .call($.fromValue(schemaCopy.minItems)); 76 + return arrayFn.call( 77 + unknownToAst({ 78 + plugin, 79 + schema: { 80 + type: 'unknown', 81 + }, 82 + }), 83 + ); 84 + } 85 + 86 + function lengthNode(ctx: ArrayResolverContext): ChainResult { 87 + const { schema } = ctx; 88 + if (schema.minItems === schema.maxItems && schema.minItems !== undefined) { 89 + return ctx.chain.current.attr(identifiers.length).call($.fromValue(schema.minItems)); 90 + } 91 + } 92 + 93 + function maxLengthNode(ctx: ArrayResolverContext): ChainResult { 94 + const { schema } = ctx; 95 + if (schema.maxItems === undefined) return; 96 + return ctx.chain.current.attr(identifiers.max).call($.fromValue(schema.maxItems)); 97 + } 98 + 99 + function minLengthNode(ctx: ArrayResolverContext): ChainResult { 100 + const { schema } = ctx; 101 + if (schema.minItems === undefined) return; 102 + return ctx.chain.current.attr(identifiers.min).call($.fromValue(schema.minItems)); 103 + } 104 + 105 + function arrayResolver(ctx: ArrayResolverContext): Chain { 106 + const baseResult = ctx.nodes.base(ctx); 107 + ctx.chain.current = baseResult; 108 + 109 + const lengthResult = ctx.nodes.length(ctx); 110 + if (lengthResult) { 111 + ctx.chain.current = lengthResult; 97 112 } else { 98 - if (schemaCopy.minItems !== undefined) { 99 - arrayExpression = arrayExpression 100 - .attr(identifiers.min) 101 - .call($.fromValue(schemaCopy.minItems)); 113 + const minLengthResult = ctx.nodes.minLength(ctx); 114 + if (minLengthResult) { 115 + ctx.chain.current = minLengthResult; 102 116 } 103 117 104 - if (schemaCopy.maxItems !== undefined) { 105 - arrayExpression = arrayExpression 106 - .attr(identifiers.max) 107 - .call($.fromValue(schemaCopy.maxItems)); 118 + const maxLengthResult = ctx.nodes.maxLength(ctx); 119 + if (maxLengthResult) { 120 + ctx.chain.current = maxLengthResult; 108 121 } 109 122 } 110 123 124 + return ctx.chain.current; 125 + } 126 + 127 + export function arrayToAst({ 128 + applyModifiers, 129 + plugin, 130 + schema, 131 + walk, 132 + walkerCtx, 133 + }: ArrayToAstOptions): CompositeHandlerResult { 134 + const childResults: Array<ZodResult> = []; 135 + let schemaCopy = schema; 136 + 137 + const z = plugin.external('zod.z'); 138 + 139 + if (schemaCopy.items) { 140 + schemaCopy = deduplicateSchema({ schema: schemaCopy }); 141 + 142 + schemaCopy.items!.forEach((item, index) => { 143 + const itemResult = walk(item, childContext(walkerCtx, 'items', index)); 144 + childResults.push(itemResult); 145 + }); 146 + } 147 + 148 + const ctx: ArrayResolverContext = { 149 + $, 150 + applyModifiers, 151 + chain: { 152 + current: $(z), 153 + }, 154 + childResults, 155 + nodes: { 156 + base: baseNode, 157 + length: lengthNode, 158 + maxLength: maxLengthNode, 159 + minLength: minLengthNode, 160 + }, 161 + plugin, 162 + schema, 163 + symbols: { 164 + z, 165 + }, 166 + walk, 167 + walkerCtx, 168 + }; 169 + 170 + const resolver = plugin.config['~resolvers']?.array; 171 + const expression = resolver?.(ctx) ?? arrayResolver(ctx); 172 + 111 173 return { 112 174 childResults, 113 - expression: arrayExpression, 175 + expression, 114 176 }; 115 177 }
+45 -6
packages/openapi-ts/src/plugins/zod/v3/toAst/boolean.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 - import type { Chain } from '../../shared/chain'; 5 + import type { BooleanResolverContext } from '../../resolvers'; 6 + import type { Chain, ChainResult } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: BooleanResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.boolean).call(); 13 + } 14 + 15 + function constNode(ctx: BooleanResolverContext): ChainResult { 16 + const { schema, symbols } = ctx; 17 + const { z } = symbols; 18 + if (typeof schema.const !== 'boolean') return; 19 + return $(z).attr(identifiers.literal).call($.literal(schema.const)); 20 + } 21 + 22 + function booleanResolver(ctx: BooleanResolverContext): Chain { 23 + const constResult = ctx.nodes.const(ctx); 24 + if (constResult) { 25 + ctx.chain.current = constResult; 26 + return ctx.chain.current; 27 + } 28 + 29 + const baseResult = ctx.nodes.base(ctx); 30 + ctx.chain.current = baseResult; 31 + 32 + return ctx.chain.current; 33 + } 34 + 8 35 export function booleanToAst({ 9 36 plugin, 10 37 schema, ··· 13 40 schema: SchemaWithType<'boolean'>; 14 41 }): Chain { 15 42 const z = plugin.external('zod.z'); 16 - 17 - if (typeof schema.const === 'boolean') { 18 - return $(z).attr(identifiers.literal).call($.literal(schema.const)); 19 - } 43 + const ctx: BooleanResolverContext = { 44 + $, 45 + chain: { 46 + current: $(z), 47 + }, 48 + nodes: { 49 + base: baseNode, 50 + const: constNode, 51 + }, 52 + plugin, 53 + schema, 54 + symbols: { 55 + z, 56 + }, 57 + }; 20 58 21 - return $(z).attr(identifiers.boolean).call(); 59 + const resolver = plugin.config['~resolvers']?.boolean; 60 + return resolver?.(ctx) ?? booleanResolver(ctx); 22 61 }
+31 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/never.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 + import type { NeverResolverContext } from '../../resolvers'; 5 6 import type { Chain } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: NeverResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.never).call(); 13 + } 14 + 15 + function neverResolver(ctx: NeverResolverContext): Chain { 16 + const baseResult = ctx.nodes.base(ctx); 17 + ctx.chain.current = baseResult; 18 + return ctx.chain.current; 19 + } 20 + 8 21 export function neverToAst({ 9 22 plugin, 23 + schema, 10 24 }: { 11 25 plugin: ZodPlugin['Instance']; 12 26 schema: SchemaWithType<'never'>; 13 27 }): Chain { 14 28 const z = plugin.external('zod.z'); 15 - return $(z).attr(identifiers.never).call(); 29 + const ctx: NeverResolverContext = { 30 + $, 31 + chain: { 32 + current: $(z), 33 + }, 34 + nodes: { 35 + base: baseNode, 36 + }, 37 + plugin, 38 + schema, 39 + symbols: { 40 + z, 41 + }, 42 + }; 43 + 44 + const resolver = plugin.config['~resolvers']?.never; 45 + return resolver?.(ctx) ?? neverResolver(ctx); 16 46 }
+31 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/null.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 + import type { NullResolverContext } from '../../resolvers'; 5 6 import type { Chain } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: NullResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.null).call(); 13 + } 14 + 15 + function nullResolver(ctx: NullResolverContext): Chain { 16 + const baseResult = ctx.nodes.base(ctx); 17 + ctx.chain.current = baseResult; 18 + return ctx.chain.current; 19 + } 20 + 8 21 export function nullToAst({ 9 22 plugin, 23 + schema, 10 24 }: { 11 25 plugin: ZodPlugin['Instance']; 12 26 schema: SchemaWithType<'null'>; 13 27 }): Chain { 14 28 const z = plugin.external('zod.z'); 15 - return $(z).attr(identifiers.null).call(); 29 + const ctx: NullResolverContext = { 30 + $, 31 + chain: { 32 + current: $(z), 33 + }, 34 + nodes: { 35 + base: baseNode, 36 + }, 37 + plugin, 38 + schema, 39 + symbols: { 40 + z, 41 + }, 42 + }; 43 + 44 + const resolver = plugin.config['~resolvers']?.null; 45 + return resolver?.(ctx) ?? nullResolver(ctx); 16 46 }
+3 -4
packages/openapi-ts/src/plugins/zod/v3/toAst/object.ts
··· 80 80 81 81 export function objectToAst(options: ObjectToAstOptions): CompositeHandlerResult { 82 82 const { applyModifiers, plugin, schema, walk, walkerCtx } = options; 83 - 84 83 const childResults: Array<ZodResult> = []; 85 - 84 + const z = plugin.external('zod.z'); 86 85 const ctx: ExtendedContext = { 87 86 $, 88 87 _childResults: childResults, 89 88 applyModifiers, 90 89 chain: { 91 - current: $(plugin.external('zod.z')), 90 + current: $(z), 92 91 }, 93 92 nodes: { 94 93 additionalProperties: additionalPropertiesNode, ··· 98 97 plugin, 99 98 schema, 100 99 symbols: { 101 - z: plugin.external('zod.z'), 100 + z, 102 101 }, 103 102 walk, 104 103 walkerCtx,
+73 -31
packages/openapi-ts/src/plugins/zod/v3/toAst/tuple.ts
··· 1 - import type { SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared'; 2 1 import { childContext } from '@hey-api/shared'; 3 2 4 3 import { $ } from '../../../../ts-dsl'; 5 4 import { identifiers } from '../../constants'; 6 - import type { Chain } from '../../shared/chain'; 7 - import type { CompositeHandlerResult, ZodFinal, ZodResult } from '../../shared/types'; 8 - import type { ZodPlugin } from '../../types'; 5 + import type { TupleResolverContext } from '../../resolvers'; 6 + import type { Chain, ChainResult } from '../../shared/chain'; 7 + import type { CompositeHandlerResult, ZodResult } from '../../shared/types'; 9 8 10 - interface TupleToAstOptions { 11 - applyModifiers: (result: ZodResult, opts: { optional?: boolean }) => ZodFinal; 12 - plugin: ZodPlugin['Instance']; 13 - schema: SchemaWithType<'tuple'>; 14 - walk: Walker<ZodResult, ZodPlugin['Instance']>; 15 - walkerCtx: SchemaVisitorContext<ZodPlugin['Instance']>; 9 + type TupleToAstOptions = Pick< 10 + TupleResolverContext, 11 + 'applyModifiers' | 'plugin' | 'schema' | 'walk' | 'walkerCtx' 12 + >; 13 + 14 + function baseNode(ctx: TupleResolverContext): Chain { 15 + const { applyModifiers, childResults, symbols } = ctx; 16 + const { z } = symbols; 17 + 18 + const tupleFn = $(z).attr(identifiers.tuple); 19 + 20 + if (childResults.length === 0) { 21 + return tupleFn.call($.array()); 22 + } 23 + 24 + const tupleElements = childResults.map( 25 + (result) => applyModifiers(result, { optional: false }).expression, 26 + ); 27 + 28 + return tupleFn.call($.array(...tupleElements)); 29 + } 30 + 31 + function constNode(ctx: TupleResolverContext): ChainResult { 32 + const { schema, symbols } = ctx; 33 + const { z } = symbols; 34 + 35 + if (!schema.const || !Array.isArray(schema.const)) return; 36 + 37 + const tupleElements = schema.const.map((value) => 38 + $(z).attr(identifiers.literal).call($.fromValue(value)), 39 + ); 40 + 41 + return $(z) 42 + .attr(identifiers.tuple) 43 + .call($.array(...tupleElements)); 44 + } 45 + 46 + function tupleResolver(ctx: TupleResolverContext): Chain { 47 + const constResult = ctx.nodes.const(ctx); 48 + if (constResult) { 49 + ctx.chain.current = constResult; 50 + return ctx.chain.current; 51 + } 52 + 53 + const baseResult = ctx.nodes.base(ctx); 54 + ctx.chain.current = baseResult; 55 + 56 + return ctx.chain.current; 16 57 } 17 58 18 59 export function tupleToAst({ ··· 26 67 27 68 const z = plugin.external('zod.z'); 28 69 29 - if (schema.const && Array.isArray(schema.const)) { 30 - const tupleElements = schema.const.map((value) => 31 - $(z).attr(identifiers.literal).call($.fromValue(value)), 32 - ); 33 - const expression = $(z) 34 - .attr(identifiers.tuple) 35 - .call($.array(...tupleElements)); 36 - return { 37 - childResults, 38 - expression, 39 - }; 40 - } 41 - 42 - const tupleElements: Array<Chain> = []; 43 - 44 70 if (schema.items) { 45 71 schema.items.forEach((item, index) => { 46 72 const itemResult = walk(item, childContext(walkerCtx, 'items', index)); 47 73 childResults.push(itemResult); 48 - 49 - const finalExpr = applyModifiers(itemResult, { optional: false }); 50 - tupleElements.push(finalExpr.expression); 51 74 }); 52 75 } 53 76 54 - const expression = $(z) 55 - .attr(identifiers.tuple) 56 - .call($.array(...tupleElements)); 77 + const ctx: TupleResolverContext = { 78 + $, 79 + applyModifiers, 80 + chain: { 81 + current: $(z), 82 + }, 83 + childResults, 84 + nodes: { 85 + base: baseNode, 86 + const: constNode, 87 + }, 88 + plugin, 89 + schema, 90 + symbols: { 91 + z, 92 + }, 93 + walk, 94 + walkerCtx, 95 + }; 96 + 97 + const resolver = plugin.config['~resolvers']?.tuple; 98 + const expression = resolver?.(ctx) ?? tupleResolver(ctx); 57 99 58 100 return { 59 101 childResults,
+31 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/undefined.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 + import type { UndefinedResolverContext } from '../../resolvers'; 5 6 import type { Chain } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: UndefinedResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.undefined).call(); 13 + } 14 + 15 + function undefinedResolver(ctx: UndefinedResolverContext): Chain { 16 + const baseResult = ctx.nodes.base(ctx); 17 + ctx.chain.current = baseResult; 18 + return ctx.chain.current; 19 + } 20 + 8 21 export function undefinedToAst({ 9 22 plugin, 23 + schema, 10 24 }: { 11 25 plugin: ZodPlugin['Instance']; 12 26 schema: SchemaWithType<'undefined'>; 13 27 }): Chain { 14 28 const z = plugin.external('zod.z'); 15 - return $(z).attr(identifiers.undefined).call(); 29 + const ctx: UndefinedResolverContext = { 30 + $, 31 + chain: { 32 + current: $(z), 33 + }, 34 + nodes: { 35 + base: baseNode, 36 + }, 37 + plugin, 38 + schema, 39 + symbols: { 40 + z, 41 + }, 42 + }; 43 + 44 + const resolver = plugin.config['~resolvers']?.undefined; 45 + return resolver?.(ctx) ?? undefinedResolver(ctx); 16 46 }
+31 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/unknown.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 + import type { UnknownResolverContext } from '../../resolvers'; 5 6 import type { Chain } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: UnknownResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.unknown).call(); 13 + } 14 + 15 + function unknownResolver(ctx: UnknownResolverContext): Chain { 16 + const baseResult = ctx.nodes.base(ctx); 17 + ctx.chain.current = baseResult; 18 + return ctx.chain.current; 19 + } 20 + 8 21 export function unknownToAst({ 9 22 plugin, 23 + schema, 10 24 }: { 11 25 plugin: ZodPlugin['Instance']; 12 26 schema: SchemaWithType<'unknown'>; 13 27 }): Chain { 14 28 const z = plugin.external('zod.z'); 15 - return $(z).attr(identifiers.unknown).call(); 29 + const ctx: UnknownResolverContext = { 30 + $, 31 + chain: { 32 + current: $(z), 33 + }, 34 + nodes: { 35 + base: baseNode, 36 + }, 37 + plugin, 38 + schema, 39 + symbols: { 40 + z, 41 + }, 42 + }; 43 + 44 + const resolver = plugin.config['~resolvers']?.unknown; 45 + return resolver?.(ctx) ?? unknownResolver(ctx); 16 46 }
+31 -1
packages/openapi-ts/src/plugins/zod/v3/toAst/void.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 + import type { VoidResolverContext } from '../../resolvers'; 5 6 import type { Chain } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: VoidResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.void).call(); 13 + } 14 + 15 + function voidResolver(ctx: VoidResolverContext): Chain { 16 + const baseResult = ctx.nodes.base(ctx); 17 + ctx.chain.current = baseResult; 18 + return ctx.chain.current; 19 + } 20 + 8 21 export function voidToAst({ 9 22 plugin, 23 + schema, 10 24 }: { 11 25 plugin: ZodPlugin['Instance']; 12 26 schema: SchemaWithType<'void'>; 13 27 }): Chain { 14 28 const z = plugin.external('zod.z'); 15 - return $(z).attr(identifiers.void).call(); 29 + const ctx: VoidResolverContext = { 30 + $, 31 + chain: { 32 + current: $(z), 33 + }, 34 + nodes: { 35 + base: baseNode, 36 + }, 37 + plugin, 38 + schema, 39 + symbols: { 40 + z, 41 + }, 42 + }; 43 + 44 + const resolver = plugin.config['~resolvers']?.void; 45 + return resolver?.(ctx) ?? voidResolver(ctx); 16 46 }
+4 -4
packages/openapi-ts/src/plugins/zod/v3/walker.ts
··· 74 74 }; 75 75 }, 76 76 array(schema, ctx, walk) { 77 - const applyModifiers = (result: ZodResult, opts: { optional?: boolean }) => 77 + const applyModifiers: Parameters<typeof arrayToAst>[0]['applyModifiers'] = (result, opts) => 78 78 this.applyModifiers(result, ctx, opts) as ZodFinal; 79 79 const { childResults, expression } = arrayToAst({ 80 80 applyModifiers, ··· 207 207 }; 208 208 }, 209 209 object(schema, ctx, walk) { 210 - const applyModifiers = (result: ZodResult, opts: { optional?: boolean }) => 210 + const applyModifiers: Parameters<typeof objectToAst>[0]['applyModifiers'] = (result, opts) => 211 211 this.applyModifiers(result, ctx, opts) as ZodFinal; 212 212 const { childResults, expression } = objectToAst({ 213 213 applyModifiers, ··· 284 284 }; 285 285 }, 286 286 tuple(schema, ctx, walk) { 287 - const applyModifiers = (result: ZodResult, opts: { optional?: boolean }) => 287 + const applyModifiers: Parameters<typeof tupleToAst>[0]['applyModifiers'] = (result, opts) => 288 288 this.applyModifiers(result, ctx, opts) as ZodFinal; 289 289 const { childResults, expression } = tupleToAst({ 290 290 applyModifiers, ··· 320 320 321 321 items.forEach((item, index) => { 322 322 const schema = schemas[index]!; 323 - if (schema.type !== 'null') { 323 + if (schema.type !== 'null' && schema.const !== null) { 324 324 nonNullItems.push(item); 325 325 } 326 326 });
+142 -80
packages/openapi-ts/src/plugins/zod/v4/toAst/array.ts
··· 1 - import type { SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared'; 2 1 import { childContext, deduplicateSchema } from '@hey-api/shared'; 3 2 4 3 import { $ } from '../../../../ts-dsl'; 5 4 import { identifiers } from '../../constants'; 6 - import type { Chain } from '../../shared/chain'; 7 - import type { CompositeHandlerResult, ZodFinal, ZodResult } from '../../shared/types'; 8 - import type { ZodPlugin } from '../../types'; 5 + import type { ArrayResolverContext } from '../../resolvers'; 6 + import type { Chain, ChainResult } from '../../shared/chain'; 7 + import type { CompositeHandlerResult, ZodResult } from '../../shared/types'; 9 8 import { unknownToAst } from './unknown'; 10 9 11 - interface ArrayToAstOptions { 12 - applyModifiers: (result: ZodResult, opts: { optional?: boolean }) => ZodFinal; 13 - plugin: ZodPlugin['Instance']; 14 - schema: SchemaWithType<'array'>; 15 - walk: Walker<ZodResult, ZodPlugin['Instance']>; 16 - walkerCtx: SchemaVisitorContext<ZodPlugin['Instance']>; 17 - } 10 + type ArrayToAstOptions = Pick< 11 + ArrayResolverContext, 12 + 'applyModifiers' | 'plugin' | 'schema' | 'walk' | 'walkerCtx' 13 + >; 18 14 19 - type AstExpression = Chain; 20 - 21 - export function arrayToAst({ 22 - applyModifiers, 23 - plugin, 24 - schema, 25 - walk, 26 - walkerCtx, 27 - }: ArrayToAstOptions): CompositeHandlerResult { 28 - const childResults: Array<ZodResult> = []; 29 - let schemaCopy = schema; 15 + function baseNode(ctx: ArrayResolverContext): Chain { 16 + const { applyModifiers, childResults, plugin, schema, symbols } = ctx; 17 + const { z } = symbols; 30 18 31 - const z = plugin.external('zod.z'); 32 - const functionName = $(z).attr(identifiers.array); 19 + const arrayFn = $(z).attr(identifiers.array); 33 20 34 - let arrayExpression: ReturnType<typeof $.call> | undefined; 21 + let normalizedSchema = schema; 22 + if (normalizedSchema.items) { 23 + normalizedSchema = deduplicateSchema({ schema: normalizedSchema }); 24 + } 35 25 36 - if (!schemaCopy.items) { 37 - arrayExpression = functionName.call( 26 + if (!normalizedSchema.items) { 27 + return arrayFn.call( 38 28 unknownToAst({ 39 29 plugin, 40 30 schema: { ··· 42 32 }, 43 33 }), 44 34 ); 45 - } else { 46 - schemaCopy = deduplicateSchema({ schema: schemaCopy }); 35 + } 47 36 48 - const itemExpressions: Array<AstExpression> = []; 37 + if (childResults.length === 1) { 38 + const itemNode = applyModifiers(childResults[0]!, { optional: false }).expression; 39 + return arrayFn.call(itemNode); 40 + } 49 41 50 - schemaCopy.items!.forEach((item, index) => { 51 - const itemResult = walk(item, childContext(walkerCtx, 'items', index)); 52 - childResults.push(itemResult); 53 - 54 - const finalExpr = applyModifiers(itemResult, { optional: false }); 55 - itemExpressions.push(finalExpr.expression); 56 - }); 42 + if (childResults.length > 1) { 43 + const itemExpressions: Array<Chain> = childResults.map( 44 + (result) => applyModifiers(result, { optional: false }).expression, 45 + ); 57 46 58 - if (itemExpressions.length === 1) { 59 - arrayExpression = functionName.call(...itemExpressions); 60 - } else { 61 - if (schemaCopy.logicalOperator === 'and') { 62 - const firstSchema = schemaCopy.items![0]!; 63 - let intersectionExpression: AstExpression; 64 - if ( 65 - firstSchema.logicalOperator === 'or' || 66 - (firstSchema.type && firstSchema.type !== 'object') 67 - ) { 68 - intersectionExpression = $(z) 69 - .attr(identifiers.intersection) 70 - .call(...itemExpressions); 71 - } else { 72 - intersectionExpression = itemExpressions[0]!; 73 - for (let i = 1; i < itemExpressions.length; i++) { 74 - intersectionExpression = intersectionExpression 75 - .attr(identifiers.and) 76 - .call(itemExpressions[i]); 77 - } 47 + const firstSchema = normalizedSchema.items[0]; 48 + if (normalizedSchema.logicalOperator === 'and') { 49 + let intersectionExpression: Chain; 50 + if ( 51 + firstSchema?.logicalOperator === 'or' || 52 + (firstSchema?.type && firstSchema.type !== 'object') 53 + ) { 54 + intersectionExpression = $(z) 55 + .attr(identifiers.intersection) 56 + .call(...itemExpressions); 57 + } else { 58 + intersectionExpression = itemExpressions[0]!; 59 + for (let i = 1; i < itemExpressions.length; i++) { 60 + intersectionExpression = intersectionExpression 61 + .attr(identifiers.and) 62 + .call(itemExpressions[i]); 78 63 } 64 + } 79 65 80 - arrayExpression = functionName.call(intersectionExpression); 81 - } else { 82 - arrayExpression = $(z) 83 - .attr(identifiers.array) 84 - .call( 85 - $(z) 86 - .attr(identifiers.union) 87 - .call($.array(...itemExpressions)), 88 - ); 89 - } 66 + return arrayFn.call(intersectionExpression); 67 + } else { 68 + return arrayFn.call( 69 + $(z) 70 + .attr(identifiers.union) 71 + .call($.array(...itemExpressions)), 72 + ); 90 73 } 91 74 } 92 75 93 - if (schemaCopy.minItems === schemaCopy.maxItems && schemaCopy.minItems !== undefined) { 94 - arrayExpression = arrayExpression 95 - .attr(identifiers.length) 96 - .call($.fromValue(schemaCopy.minItems)); 76 + return arrayFn.call( 77 + unknownToAst({ 78 + plugin, 79 + schema: { 80 + type: 'unknown', 81 + }, 82 + }), 83 + ); 84 + } 85 + 86 + function lengthNode(ctx: ArrayResolverContext): ChainResult { 87 + const { schema } = ctx; 88 + if (schema.minItems === schema.maxItems && schema.minItems !== undefined) { 89 + return ctx.chain.current.attr(identifiers.length).call($.fromValue(schema.minItems)); 90 + } 91 + } 92 + 93 + function maxLengthNode(ctx: ArrayResolverContext): ChainResult { 94 + const { schema } = ctx; 95 + if (schema.maxItems === undefined) return; 96 + return ctx.chain.current.attr(identifiers.max).call($.fromValue(schema.maxItems)); 97 + } 98 + 99 + function minLengthNode(ctx: ArrayResolverContext): ChainResult { 100 + const { schema } = ctx; 101 + if (schema.minItems === undefined) return; 102 + return ctx.chain.current.attr(identifiers.min).call($.fromValue(schema.minItems)); 103 + } 104 + 105 + function arrayResolver(ctx: ArrayResolverContext): Chain { 106 + const baseResult = ctx.nodes.base(ctx); 107 + ctx.chain.current = baseResult; 108 + 109 + const lengthResult = ctx.nodes.length(ctx); 110 + if (lengthResult) { 111 + ctx.chain.current = lengthResult; 97 112 } else { 98 - if (schemaCopy.minItems !== undefined) { 99 - arrayExpression = arrayExpression 100 - .attr(identifiers.min) 101 - .call($.fromValue(schemaCopy.minItems)); 113 + const minLengthResult = ctx.nodes.minLength(ctx); 114 + if (minLengthResult) { 115 + ctx.chain.current = minLengthResult; 102 116 } 103 117 104 - if (schemaCopy.maxItems !== undefined) { 105 - arrayExpression = arrayExpression 106 - .attr(identifiers.max) 107 - .call($.fromValue(schemaCopy.maxItems)); 118 + const maxLengthResult = ctx.nodes.maxLength(ctx); 119 + if (maxLengthResult) { 120 + ctx.chain.current = maxLengthResult; 108 121 } 109 122 } 110 123 124 + return ctx.chain.current; 125 + } 126 + 127 + export function arrayToAst({ 128 + applyModifiers, 129 + plugin, 130 + schema, 131 + walk, 132 + walkerCtx, 133 + }: ArrayToAstOptions): CompositeHandlerResult { 134 + const childResults: Array<ZodResult> = []; 135 + let schemaCopy = schema; 136 + 137 + const z = plugin.external('zod.z'); 138 + 139 + if (schemaCopy.items) { 140 + schemaCopy = deduplicateSchema({ schema: schemaCopy }); 141 + 142 + schemaCopy.items!.forEach((item, index) => { 143 + const itemResult = walk(item, childContext(walkerCtx, 'items', index)); 144 + childResults.push(itemResult); 145 + }); 146 + } 147 + 148 + const ctx: ArrayResolverContext = { 149 + $, 150 + applyModifiers, 151 + chain: { 152 + current: $(z), 153 + }, 154 + childResults, 155 + nodes: { 156 + base: baseNode, 157 + length: lengthNode, 158 + maxLength: maxLengthNode, 159 + minLength: minLengthNode, 160 + }, 161 + plugin, 162 + schema, 163 + symbols: { 164 + z, 165 + }, 166 + walk, 167 + walkerCtx, 168 + }; 169 + 170 + const resolver = plugin.config['~resolvers']?.array; 171 + const expression = resolver?.(ctx) ?? arrayResolver(ctx); 172 + 111 173 return { 112 174 childResults, 113 - expression: arrayExpression, 175 + expression, 114 176 }; 115 177 }
+45 -6
packages/openapi-ts/src/plugins/zod/v4/toAst/boolean.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 - import type { Chain } from '../../shared/chain'; 5 + import type { BooleanResolverContext } from '../../resolvers'; 6 + import type { Chain, ChainResult } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: BooleanResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.boolean).call(); 13 + } 14 + 15 + function constNode(ctx: BooleanResolverContext): ChainResult { 16 + const { schema, symbols } = ctx; 17 + const { z } = symbols; 18 + if (typeof schema.const !== 'boolean') return; 19 + return $(z).attr(identifiers.literal).call($.literal(schema.const)); 20 + } 21 + 22 + function booleanResolver(ctx: BooleanResolverContext): Chain { 23 + const constResult = ctx.nodes.const(ctx); 24 + if (constResult) { 25 + ctx.chain.current = constResult; 26 + return ctx.chain.current; 27 + } 28 + 29 + const baseResult = ctx.nodes.base(ctx); 30 + ctx.chain.current = baseResult; 31 + 32 + return ctx.chain.current; 33 + } 34 + 8 35 export function booleanToAst({ 9 36 plugin, 10 37 schema, ··· 13 40 schema: SchemaWithType<'boolean'>; 14 41 }): Chain { 15 42 const z = plugin.external('zod.z'); 16 - 17 - if (typeof schema.const === 'boolean') { 18 - return $(z).attr(identifiers.literal).call($.literal(schema.const)); 19 - } 43 + const ctx: BooleanResolverContext = { 44 + $, 45 + chain: { 46 + current: $(z), 47 + }, 48 + nodes: { 49 + base: baseNode, 50 + const: constNode, 51 + }, 52 + plugin, 53 + schema, 54 + symbols: { 55 + z, 56 + }, 57 + }; 20 58 21 - return $(z).attr(identifiers.boolean).call(); 59 + const resolver = plugin.config['~resolvers']?.boolean; 60 + return resolver?.(ctx) ?? booleanResolver(ctx); 22 61 }
+80
packages/openapi-ts/src/plugins/zod/v4/toAst/intersection.ts
··· 1 + import type { IR } from '@hey-api/shared'; 2 + import { childContext } from '@hey-api/shared'; 3 + 4 + import { $ } from '../../../../ts-dsl'; 5 + import { identifiers } from '../../constants'; 6 + import type { IntersectionResolverContext } from '../../resolvers'; 7 + import type { Chain } from '../../shared/chain'; 8 + import type { ZodResult } from '../../shared/types'; 9 + 10 + type IntersectionToAstOptions = Pick< 11 + IntersectionResolverContext, 12 + 'parentSchema' | 'plugin' | 'walk' | 'walkerCtx' 13 + > & { 14 + schemas: ReadonlyArray<IR.SchemaObject>; 15 + }; 16 + 17 + function baseNode(ctx: IntersectionResolverContext): Chain { 18 + const { childResults, symbols } = ctx; 19 + const { z } = symbols; 20 + 21 + if (childResults.length === 0) { 22 + return $(z).attr(identifiers.never).call(); 23 + } 24 + 25 + return $(z) 26 + .attr(identifiers.intersection) 27 + .call(...childResults.map((result) => result.expression)); 28 + } 29 + 30 + function intersectionResolver(ctx: IntersectionResolverContext): Chain { 31 + const baseResult = ctx.nodes.base(ctx); 32 + ctx.chain.current = baseResult; 33 + return ctx.chain.current; 34 + } 35 + 36 + export function intersectionToAst({ 37 + parentSchema, 38 + plugin, 39 + schemas, 40 + walk, 41 + walkerCtx, 42 + }: IntersectionToAstOptions): { 43 + childResults: Array<ZodResult>; 44 + expression: Chain; 45 + } { 46 + const z = plugin.external('zod.z'); 47 + 48 + const childResults: Array<ZodResult> = []; 49 + schemas.forEach((schema, index) => { 50 + const itemResult = walk(schema, childContext(walkerCtx, 'allOf', index)); 51 + childResults.push(itemResult); 52 + }); 53 + 54 + const ctx: IntersectionResolverContext = { 55 + $, 56 + chain: { 57 + current: $(z), 58 + }, 59 + childResults, 60 + nodes: { 61 + base: baseNode, 62 + }, 63 + parentSchema, 64 + plugin, 65 + schema: parentSchema, 66 + symbols: { 67 + z, 68 + }, 69 + walk, 70 + walkerCtx, 71 + }; 72 + 73 + const resolver = plugin.config['~resolvers']?.intersection; 74 + const expression = resolver?.(ctx) ?? intersectionResolver(ctx); 75 + 76 + return { 77 + childResults, 78 + expression, 79 + }; 80 + }
+31 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/never.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 + import type { NeverResolverContext } from '../../resolvers'; 5 6 import type { Chain } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: NeverResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.never).call(); 13 + } 14 + 15 + function neverResolver(ctx: NeverResolverContext): Chain { 16 + const baseResult = ctx.nodes.base(ctx); 17 + ctx.chain.current = baseResult; 18 + return ctx.chain.current; 19 + } 20 + 8 21 export function neverToAst({ 9 22 plugin, 23 + schema, 10 24 }: { 11 25 plugin: ZodPlugin['Instance']; 12 26 schema: SchemaWithType<'never'>; 13 27 }): Chain { 14 28 const z = plugin.external('zod.z'); 15 - return $(z).attr(identifiers.never).call(); 29 + const ctx: NeverResolverContext = { 30 + $, 31 + chain: { 32 + current: $(z), 33 + }, 34 + nodes: { 35 + base: baseNode, 36 + }, 37 + plugin, 38 + schema, 39 + symbols: { 40 + z, 41 + }, 42 + }; 43 + 44 + const resolver = plugin.config['~resolvers']?.never; 45 + return resolver?.(ctx) ?? neverResolver(ctx); 16 46 }
+31 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/null.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 + import type { NullResolverContext } from '../../resolvers'; 5 6 import type { Chain } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: NullResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.null).call(); 13 + } 14 + 15 + function nullResolver(ctx: NullResolverContext): Chain { 16 + const baseResult = ctx.nodes.base(ctx); 17 + ctx.chain.current = baseResult; 18 + return ctx.chain.current; 19 + } 20 + 8 21 export function nullToAst({ 9 22 plugin, 23 + schema, 10 24 }: { 11 25 plugin: ZodPlugin['Instance']; 12 26 schema: SchemaWithType<'null'>; 13 27 }): Chain { 14 28 const z = plugin.external('zod.z'); 15 - return $(z).attr(identifiers.null).call(); 29 + const ctx: NullResolverContext = { 30 + $, 31 + chain: { 32 + current: $(z), 33 + }, 34 + nodes: { 35 + base: baseNode, 36 + }, 37 + plugin, 38 + schema, 39 + symbols: { 40 + z, 41 + }, 42 + }; 43 + 44 + const resolver = plugin.config['~resolvers']?.null; 45 + return resolver?.(ctx) ?? nullResolver(ctx); 16 46 }
+3 -4
packages/openapi-ts/src/plugins/zod/v4/toAst/object.ts
··· 84 84 85 85 export function objectToAst(options: ObjectToAstOptions): CompositeHandlerResult { 86 86 const { applyModifiers, plugin, schema, walk, walkerCtx } = options; 87 - 88 87 const childResults: Array<ZodResult> = []; 89 - 88 + const z = plugin.external('zod.z'); 90 89 const ctx: ExtendedContext = { 91 90 $, 92 91 _childResults: childResults, 93 92 applyModifiers, 94 93 chain: { 95 - current: $(plugin.external('zod.z')), 94 + current: $(z), 96 95 }, 97 96 nodes: { 98 97 additionalProperties: additionalPropertiesNode, ··· 102 101 plugin, 103 102 schema, 104 103 symbols: { 105 - z: plugin.external('zod.z'), 104 + z, 106 105 }, 107 106 walk, 108 107 walkerCtx,
+75 -30
packages/openapi-ts/src/plugins/zod/v4/toAst/tuple.ts
··· 1 - import type { SchemaVisitorContext, SchemaWithType, Walker } from '@hey-api/shared'; 2 1 import { childContext } from '@hey-api/shared'; 3 2 4 3 import { $ } from '../../../../ts-dsl'; 5 4 import { identifiers } from '../../constants'; 6 - import type { Chain } from '../../shared/chain'; 7 - import type { CompositeHandlerResult, ZodFinal, ZodResult } from '../../shared/types'; 8 - import type { ZodPlugin } from '../../types'; 5 + import type { TupleResolverContext } from '../../resolvers'; 6 + import type { Chain, ChainResult } from '../../shared/chain'; 7 + import type { CompositeHandlerResult, ZodResult } from '../../shared/types'; 8 + 9 + type TupleToAstOptions = Pick< 10 + TupleResolverContext, 11 + 'applyModifiers' | 'plugin' | 'schema' | 'walk' | 'walkerCtx' 12 + >; 13 + 14 + function baseNode(ctx: TupleResolverContext): Chain { 15 + const { applyModifiers, childResults, symbols } = ctx; 16 + const { z } = symbols; 17 + 18 + const tupleFn = $(z).attr(identifiers.tuple); 19 + 20 + if (childResults.length === 0) { 21 + return tupleFn.call($.array()); 22 + } 23 + 24 + const tupleElements = childResults.map( 25 + (result) => applyModifiers(result, { optional: false }).expression, 26 + ); 27 + 28 + return tupleFn.call($.array(...tupleElements)); 29 + } 30 + 31 + function constNode(ctx: TupleResolverContext): ChainResult { 32 + const { schema, symbols } = ctx; 33 + const { z } = symbols; 34 + 35 + if (!schema.const || !Array.isArray(schema.const)) return; 36 + 37 + const tupleElements = schema.const.map((value) => 38 + $(z).attr(identifiers.literal).call($.fromValue(value)), 39 + ); 40 + 41 + return $(z) 42 + .attr(identifiers.tuple) 43 + .call($.array(...tupleElements)); 44 + } 45 + 46 + function tupleResolver(ctx: TupleResolverContext): Chain { 47 + const constResult = ctx.nodes.const(ctx); 48 + if (constResult) { 49 + ctx.chain.current = constResult; 50 + return ctx.chain.current; 51 + } 52 + 53 + const baseResult = ctx.nodes.base(ctx); 54 + ctx.chain.current = baseResult; 9 55 10 - interface TupleToAstOptions { 11 - applyModifiers: (result: ZodResult, opts: { optional?: boolean }) => ZodFinal; 12 - plugin: ZodPlugin['Instance']; 13 - schema: SchemaWithType<'tuple'>; 14 - walk: Walker<ZodResult, ZodPlugin['Instance']>; 15 - walkerCtx: SchemaVisitorContext<ZodPlugin['Instance']>; 56 + return ctx.chain.current; 16 57 } 17 58 18 59 export function tupleToAst({ ··· 26 67 27 68 const z = plugin.external('zod.z'); 28 69 29 - if (schema.const && Array.isArray(schema.const)) { 30 - const tupleElements = schema.const.map((value) => 31 - $(z).attr(identifiers.literal).call($.fromValue(value)), 32 - ); 33 - return { 34 - childResults, 35 - expression: $(z) 36 - .attr(identifiers.tuple) 37 - .call($.array(...tupleElements)), 38 - }; 39 - } 40 - 41 - const tupleElements: Array<Chain> = []; 42 - 43 70 if (schema.items) { 44 71 schema.items.forEach((item, index) => { 45 72 const itemResult = walk(item, childContext(walkerCtx, 'items', index)); 46 73 childResults.push(itemResult); 47 - 48 - const finalExpr = applyModifiers(itemResult, { optional: false }); 49 - tupleElements.push(finalExpr.expression); 50 74 }); 51 75 } 52 76 77 + const ctx: TupleResolverContext = { 78 + $, 79 + applyModifiers, 80 + chain: { 81 + current: $(z), 82 + }, 83 + childResults, 84 + nodes: { 85 + base: baseNode, 86 + const: constNode, 87 + }, 88 + plugin, 89 + schema, 90 + symbols: { 91 + z, 92 + }, 93 + walk, 94 + walkerCtx, 95 + }; 96 + 97 + const resolver = plugin.config['~resolvers']?.tuple; 98 + const expression = resolver?.(ctx) ?? tupleResolver(ctx); 99 + 53 100 return { 54 101 childResults, 55 - expression: $(z) 56 - .attr(identifiers.tuple) 57 - .call($.array(...tupleElements)), 102 + expression, 58 103 }; 59 104 }
+31 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/undefined.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 + import type { UndefinedResolverContext } from '../../resolvers'; 5 6 import type { Chain } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: UndefinedResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.undefined).call(); 13 + } 14 + 15 + function undefinedResolver(ctx: UndefinedResolverContext): Chain { 16 + const baseResult = ctx.nodes.base(ctx); 17 + ctx.chain.current = baseResult; 18 + return ctx.chain.current; 19 + } 20 + 8 21 export function undefinedToAst({ 9 22 plugin, 23 + schema, 10 24 }: { 11 25 plugin: ZodPlugin['Instance']; 12 26 schema: SchemaWithType<'undefined'>; 13 27 }): Chain { 14 28 const z = plugin.external('zod.z'); 15 - return $(z).attr(identifiers.undefined).call(); 29 + const ctx: UndefinedResolverContext = { 30 + $, 31 + chain: { 32 + current: $(z), 33 + }, 34 + nodes: { 35 + base: baseNode, 36 + }, 37 + plugin, 38 + schema, 39 + symbols: { 40 + z, 41 + }, 42 + }; 43 + 44 + const resolver = plugin.config['~resolvers']?.undefined; 45 + return resolver?.(ctx) ?? undefinedResolver(ctx); 16 46 }
+93
packages/openapi-ts/src/plugins/zod/v4/toAst/union.ts
··· 1 + import { childContext } from '@hey-api/shared'; 2 + 3 + import { $ } from '../../../../ts-dsl'; 4 + import { identifiers } from '../../constants'; 5 + import type { UnionResolverContext } from '../../resolvers'; 6 + import type { Chain } from '../../shared/chain'; 7 + import type { ZodResult } from '../../shared/types'; 8 + 9 + type UnionToAstOptions = Pick< 10 + UnionResolverContext, 11 + 'parentSchema' | 'plugin' | 'schemas' | 'walk' | 'walkerCtx' 12 + >; 13 + 14 + function baseNode(ctx: UnionResolverContext): Chain { 15 + const { childResults, schemas, symbols } = ctx; 16 + const { z } = symbols; 17 + 18 + if (childResults.length === 0) { 19 + return $(z).attr(identifiers.null).call(); 20 + } 21 + 22 + const nonNullItems: Array<ZodResult> = []; 23 + childResults.forEach((result, index) => { 24 + const schema = schemas[index]!; 25 + if (schema.type !== 'null' && schema.const !== null) { 26 + nonNullItems.push(result); 27 + } 28 + }); 29 + 30 + let expression: Chain; 31 + if (nonNullItems.length === 0) { 32 + expression = $(z).attr(identifiers.null).call(); 33 + } else if (nonNullItems.length === 1) { 34 + expression = nonNullItems[0]!.expression; 35 + } else { 36 + expression = $(z) 37 + .attr(identifiers.union) 38 + .call( 39 + $.array() 40 + .pretty() 41 + .elements(...nonNullItems.map((item) => item.expression)), 42 + ); 43 + } 44 + 45 + return expression; 46 + } 47 + 48 + function unionResolver(ctx: UnionResolverContext): Chain { 49 + const baseResult = ctx.nodes.base(ctx); 50 + ctx.chain.current = baseResult; 51 + return ctx.chain.current; 52 + } 53 + 54 + export function unionToAst({ parentSchema, plugin, schemas, walk, walkerCtx }: UnionToAstOptions): { 55 + childResults: Array<ZodResult>; 56 + expression: Chain; 57 + } { 58 + const z = plugin.external('zod.z'); 59 + 60 + const childResults: Array<ZodResult> = []; 61 + schemas.forEach((schema, index) => { 62 + const itemResult = walk(schema, childContext(walkerCtx, 'anyOf', index)); 63 + childResults.push(itemResult); 64 + }); 65 + 66 + const ctx: UnionResolverContext = { 67 + $, 68 + chain: { 69 + current: $(z), 70 + }, 71 + childResults, 72 + nodes: { 73 + base: baseNode, 74 + }, 75 + parentSchema, 76 + plugin, 77 + schema: parentSchema, 78 + schemas, 79 + symbols: { 80 + z, 81 + }, 82 + walk, 83 + walkerCtx, 84 + }; 85 + 86 + const resolver = plugin.config['~resolvers']?.union; 87 + const expression = resolver?.(ctx) ?? unionResolver(ctx); 88 + 89 + return { 90 + childResults, 91 + expression, 92 + }; 93 + }
+31 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/unknown.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 + import type { UnknownResolverContext } from '../../resolvers'; 5 6 import type { Chain } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: UnknownResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.unknown).call(); 13 + } 14 + 15 + function unknownResolver(ctx: UnknownResolverContext): Chain { 16 + const baseResult = ctx.nodes.base(ctx); 17 + ctx.chain.current = baseResult; 18 + return ctx.chain.current; 19 + } 20 + 8 21 export function unknownToAst({ 9 22 plugin, 23 + schema, 10 24 }: { 11 25 plugin: ZodPlugin['Instance']; 12 26 schema: SchemaWithType<'unknown'>; 13 27 }): Chain { 14 28 const z = plugin.external('zod.z'); 15 - return $(z).attr(identifiers.unknown).call(); 29 + const ctx: UnknownResolverContext = { 30 + $, 31 + chain: { 32 + current: $(z), 33 + }, 34 + nodes: { 35 + base: baseNode, 36 + }, 37 + plugin, 38 + schema, 39 + symbols: { 40 + z, 41 + }, 42 + }; 43 + 44 + const resolver = plugin.config['~resolvers']?.unknown; 45 + return resolver?.(ctx) ?? unknownResolver(ctx); 16 46 }
+31 -1
packages/openapi-ts/src/plugins/zod/v4/toAst/void.ts
··· 2 2 3 3 import { $ } from '../../../../ts-dsl'; 4 4 import { identifiers } from '../../constants'; 5 + import type { VoidResolverContext } from '../../resolvers'; 5 6 import type { Chain } from '../../shared/chain'; 6 7 import type { ZodPlugin } from '../../types'; 7 8 9 + function baseNode(ctx: VoidResolverContext): Chain { 10 + const { symbols } = ctx; 11 + const { z } = symbols; 12 + return $(z).attr(identifiers.void).call(); 13 + } 14 + 15 + function voidResolver(ctx: VoidResolverContext): Chain { 16 + const baseResult = ctx.nodes.base(ctx); 17 + ctx.chain.current = baseResult; 18 + return ctx.chain.current; 19 + } 20 + 8 21 export function voidToAst({ 9 22 plugin, 23 + schema, 10 24 }: { 11 25 plugin: ZodPlugin['Instance']; 12 26 schema: SchemaWithType<'void'>; 13 27 }): Chain { 14 28 const z = plugin.external('zod.z'); 15 - return $(z).attr(identifiers.void).call(); 29 + const ctx: VoidResolverContext = { 30 + $, 31 + chain: { 32 + current: $(z), 33 + }, 34 + nodes: { 35 + base: baseNode, 36 + }, 37 + plugin, 38 + schema, 39 + symbols: { 40 + z, 41 + }, 42 + }; 43 + 44 + const resolver = plugin.config['~resolvers']?.void; 45 + return resolver?.(ctx) ?? voidResolver(ctx); 16 46 }
+4 -4
packages/openapi-ts/src/plugins/zod/v4/walker.ts
··· 69 69 }; 70 70 }, 71 71 array(schema, ctx, walk) { 72 - const applyModifiers = (result: ZodResult, opts: { optional?: boolean }) => 72 + const applyModifiers: Parameters<typeof arrayToAst>[0]['applyModifiers'] = (result, opts) => 73 73 this.applyModifiers(result, ctx, opts) as ZodFinal; 74 74 const { childResults, expression } = arrayToAst({ 75 75 applyModifiers, ··· 202 202 }; 203 203 }, 204 204 object(schema, ctx, walk) { 205 - const applyModifiers = (result: ZodResult, opts: { optional?: boolean }) => 205 + const applyModifiers: Parameters<typeof objectToAst>[0]['applyModifiers'] = (result, opts) => 206 206 this.applyModifiers(result, ctx, opts) as ZodFinal; 207 207 const { childResults, expression } = objectToAst({ 208 208 applyModifiers, ··· 296 296 }; 297 297 }, 298 298 tuple(schema, ctx, walk) { 299 - const applyModifiers = (result: ZodResult, opts: { optional?: boolean }) => 299 + const applyModifiers: Parameters<typeof tupleToAst>[0]['applyModifiers'] = (result, opts) => 300 300 this.applyModifiers(result, ctx, opts) as ZodFinal; 301 301 const { childResults, expression } = tupleToAst({ 302 302 applyModifiers, ··· 332 332 333 333 items.forEach((item, index) => { 334 334 const schema = schemas[index]!; 335 - if (schema.type !== 'null') { 335 + if (schema.type !== 'null' && schema.const !== null) { 336 336 nonNullItems.push(item); 337 337 } 338 338 });