diff --git a/docs/src/pages/reference/configuration/output.md b/docs/src/pages/reference/configuration/output.md index 8f09d886c..621d8bf87 100644 --- a/docs/src/pages/reference/configuration/output.md +++ b/docs/src/pages/reference/configuration/output.md @@ -927,6 +927,42 @@ module.exports = { }; ``` +#### zod + +Type: `Object`. + +Give you specific options for the zod client + +```js +module.exports = { + petstore: { + output: { + ... + override: { + zod: { + strict: { + response: true, + query: true, + param: true, + header: true, + body: true + }, + }, + }, + }, + ... + }, +}; +``` + +##### strict + +Type: `Object`. + +Default Value: `false`. + +Use to set the strict mode for the zod schema. If you set it to true, the schema will be generated with the strict mode. + #### mock Type: `Object`. diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 9c7398a62..02105622a 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -96,6 +96,7 @@ export type NormalizedOverrideOutput = { query: NormalizedQueryOptions; angular: Required; swr: SwrOptions; + zod: NormalizedZodOptions; operationName?: ( operation: OperationObject, route: string, @@ -129,6 +130,7 @@ export type NormalizedOperationOptions = { query?: NormalizedQueryOptions; angular?: Required; swr?: SwrOptions; + zod?: NormalizedZodOptions; operationName?: ( operation: OperationObject, route: string, @@ -329,6 +331,7 @@ export type OverrideOutput = { query?: QueryOptions; swr?: SwrOptions; angular?: AngularOptions; + zod?: ZodOptions; operationName?: ( operation: OperationObject, route: string, @@ -352,6 +355,26 @@ export type NormalizedHonoOptions = { handlers?: string; }; +export type ZodOptions = { + strict?: { + param?: boolean; + query?: boolean; + header?: boolean; + body?: boolean; + response?: boolean; + }; +}; + +export type NormalizedZodOptions = { + strict: { + param: boolean; + query: boolean; + header: boolean; + body: boolean; + response: boolean; + }; +}; + export type HonoOptions = { handlers?: string; }; @@ -419,6 +442,7 @@ export type OperationOptions = { query?: QueryOptions; angular?: Required; swr?: SwrOptions; + zod?: ZodOptions; operationName?: ( operation: OperationObject, route: string, diff --git a/packages/hono/src/index.ts b/packages/hono/src/index.ts index 78ad27a97..94c186142 100644 --- a/packages/hono/src/index.ts +++ b/packages/hono/src/index.ts @@ -199,14 +199,17 @@ const getHandlerFix = ({ const getVerbOptionGroupByTag = ( verbOptions: Record, ) => { - return Object.values(verbOptions).reduce((acc, value) => { - const tag = value.tags[0]; - if (!acc[tag]) { - acc[tag] = []; - } - acc[tag].push(value); - return acc; - }, {} as Record); + return Object.values(verbOptions).reduce( + (acc, value) => { + const tag = value.tags[0]; + if (!acc[tag]) { + acc[tag] = []; + } + acc[tag].push(value); + return acc; + }, + {} as Record, + ); }; const generateHandlers = async ( diff --git a/packages/orval/src/utils/options.ts b/packages/orval/src/utils/options.ts index dabdb6943..e11b98de0 100644 --- a/packages/orval/src/utils/options.ts +++ b/packages/orval/src/utils/options.ts @@ -212,6 +212,15 @@ export const normalizeOptions = async ( shouldExportMutatorHooks: true, ...normalizeQueryOptions(outputOptions.override?.query, workspace), }, + zod: { + strict: { + param: outputOptions.override?.zod?.strict?.param ?? false, + query: outputOptions.override?.zod?.strict?.query ?? false, + header: outputOptions.override?.zod?.strict?.header ?? false, + body: outputOptions.override?.zod?.strict?.body ?? false, + response: outputOptions.override?.zod?.strict?.response ?? false, + }, + }, swr: { ...(outputOptions.override?.swr ?? {}), }, @@ -310,6 +319,7 @@ const normalizeOperationsAndTags = ( formUrlEncoded, paramsSerializer, query, + zod, ...rest }, ]) => { @@ -322,6 +332,19 @@ const normalizeOperationsAndTags = ( query: normalizeQueryOptions(query, workspace), } : {}), + ...(zod + ? { + zod: { + strict: { + param: zod.strict?.param ?? false, + query: zod.strict?.query ?? false, + header: zod.strict?.header ?? false, + body: zod.strict?.body ?? false, + response: zod.strict?.response ?? false, + }, + }, + } + : {}), ...(transformer ? { transformer: normalizePath(transformer, workspace) } : {}), diff --git a/packages/zod/src/index.ts b/packages/zod/src/index.ts index d9ee470af..64e1dc0f4 100644 --- a/packages/zod/src/index.ts +++ b/packages/zod/src/index.ts @@ -57,6 +57,7 @@ const generateZodValidationSchemaDefinition = ( schema: SchemaObject | undefined, _required: boolean | undefined, name: string, + strict: boolean, ): { functions: [string, any][]; consts: string[] } => { if (!schema) return { functions: [], consts: [] }; @@ -84,7 +85,7 @@ const generateZodValidationSchemaDefinition = ( const items = schema.items as SchemaObject | undefined; functions.push([ 'array', - generateZodValidationSchemaDefinition(items, true, camel(name)), + generateZodValidationSchemaDefinition(items, true, camel(name), strict), ]); break; case 'string': { @@ -142,6 +143,7 @@ const generateZodValidationSchemaDefinition = ( schema as SchemaObject, true, camel(name), + strict, ), ), ]); @@ -157,11 +159,16 @@ const generateZodValidationSchemaDefinition = ( schema.properties?.[key] as any, schema.required?.includes(key), camel(`${name}-${key}`), + strict, ), })) .reduce((acc, curr) => ({ ...acc, ...curr }), {}), ]); + if (strict) { + functions.push(['strict', undefined]); + } + break; } @@ -174,6 +181,7 @@ const generateZodValidationSchemaDefinition = ( schema.additionalProperties as SchemaObject, true, name, + strict, ), ]); @@ -181,6 +189,7 @@ const generateZodValidationSchemaDefinition = ( } functions.push([type as string, undefined]); + break; } } @@ -240,6 +249,7 @@ export type ZodValidationSchemaDefinitionInput = Record< export const parseZodValidationSchemaDefinition = ( input: ZodValidationSchemaDefinitionInput, + strict: boolean, coerceTypes = false, ): { zod: string; consts: string } => { if (!Object.keys(input).length) { @@ -296,7 +306,7 @@ export const parseZodValidationSchemaDefinition = ( } if (fn === 'object') { - const parsed = parseZodValidationSchemaDefinition(args); + const parsed = parseZodValidationSchemaDefinition(args, strict); consts += parsed.consts; return ` ${parsed.zod}`; } @@ -310,6 +320,10 @@ export const parseZodValidationSchemaDefinition = ( return `.array(${value.startsWith('.') ? 'zod' : ''}${value})`; } + if (fn === 'strict') { + return '.strict()'; + } + if (coerceTypes && COERCEABLE_TYPES.includes(fn)) { return `.coerce.${fn}(${args})`; } @@ -328,7 +342,7 @@ ${Object.entries(input) return ` "${key}": ${value.startsWith('.') ? 'zod' : ''}${value}`; }) .join(',\n')} -})`; +})${strict ? '.strict()' : ''}`; return { zod, consts }; }; @@ -371,8 +385,8 @@ const deference = ( }; const generateZodRoute = ( - { operationName, verb }: GeneratorVerbOptions, - { pathRoute, context, override }: GeneratorOptions, + { operationName, verb, override }: GeneratorVerbOptions, + { pathRoute, context }: GeneratorOptions, ) => { const spec = context.specs[context.specKey].paths[pathRoute] as | PathItemObject @@ -414,6 +428,7 @@ const generateZodRoute = ( (requiredKey: string) => requiredKey === key, ), camel(`${operationName}-response-${key}`), + override.zod.strict.response, ), }; }) @@ -447,6 +462,7 @@ const generateZodRoute = ( (requiredKey: string) => requiredKey === key, ), camel(`${operationName}-body-${key}`), + override.zod.strict.body, ), }; }) @@ -462,10 +478,17 @@ const generateZodRoute = ( const schema = deference(parameter.schema, context); + const strict = { + path: override.zod.strict.param, + query: override.zod.strict.query, + header: override.zod.strict.header, + }; + const definition = generateZodValidationSchemaDefinition( schema, parameter.required, camel(`${operationName}-${parameter.in}-${parameter.name}`), + strict[parameter.in as 'path' | 'query' | 'header'] ?? false, ); if (parameter.in === 'header') { @@ -503,17 +526,24 @@ const generateZodRoute = ( const inputParams = parseZodValidationSchemaDefinition( zodDefinitionsParameters.params, + override.zod.strict.param, ); const inputQueryParams = parseZodValidationSchemaDefinition( zodDefinitionsParameters.queryParams, + override.zod.strict.query, override.coerceTypes, ); const inputHeaders = parseZodValidationSchemaDefinition( zodDefinitionsParameters.headers, + override.zod.strict.header, + ); + const inputBody = parseZodValidationSchemaDefinition( + zodDefinitionsBody, + override.zod.strict.body, ); - const inputBody = parseZodValidationSchemaDefinition(zodDefinitionsBody); const inputResponse = parseZodValidationSchemaDefinition( zodDefinitionsResponse, + override.zod.strict.response, ); if ( diff --git a/packages/zod/src/zod.test.ts b/packages/zod/src/zod.test.ts index cf29f6ff9..c1b8bdae1 100644 --- a/packages/zod/src/zod.test.ts +++ b/packages/zod/src/zod.test.ts @@ -34,7 +34,11 @@ const queryParams: ZodValidationSchemaDefinitionInput = { describe('parseZodValidationSchemaDefinition', () => { describe('with `override.coerceTypes = false` (default)', () => { it('does not emit coerced zod property schemas', () => { - const parseResult = parseZodValidationSchemaDefinition(queryParams); + const parseResult = parseZodValidationSchemaDefinition( + queryParams, + false, + false, + ); expect(parseResult.zod).toBe( 'zod.object({\n "limit": zod.number().optional().null(),\n "q": zod.array(zod.string()).optional()\n})', @@ -44,11 +48,25 @@ describe('parseZodValidationSchemaDefinition', () => { describe('with `override.coerceTypes = true`', () => { it('emits coerced zod property schemas', () => { - const parseResult = parseZodValidationSchemaDefinition(queryParams, true); + const parseResult = parseZodValidationSchemaDefinition( + queryParams, + false, + true, + ); expect(parseResult.zod).toBe( 'zod.object({\n "limit": zod.coerce.number().optional().null(),\n "q": zod.array(zod.coerce.string()).optional()\n})', ); }); }); + + describe('with `strict = true`', () => { + it('emits coerced zod property schemas', () => { + const parseResult = parseZodValidationSchemaDefinition(queryParams, true); + + expect(parseResult.zod).toBe( + 'zod.object({\n "limit": zod.number().optional().null(),\n "q": zod.array(zod.string()).optional()\n}).strict()', + ); + }); + }); }); diff --git a/samples/hono/orval.config.ts b/samples/hono/orval.config.ts index 07bc0c4e5..2ced41806 100644 --- a/samples/hono/orval.config.ts +++ b/samples/hono/orval.config.ts @@ -10,6 +10,11 @@ export default defineConfig({ hono: { handlers: 'src/handlers', }, + zod: { + strict: { + response: true, + }, + }, }, }, input: { diff --git a/samples/hono/package.json b/samples/hono/package.json index 2c9be4ddf..1f8d98ceb 100644 --- a/samples/hono/package.json +++ b/samples/hono/package.json @@ -6,7 +6,8 @@ }, "dependencies": { "@hono/zod-validator": "^0.2.0", - "hono": "^4.0.4" + "hono": "^4.0.4", + "zod": "^3.22.4" }, "devDependencies": { "@cloudflare/workers-types": "^4.20240208.0", diff --git a/samples/hono/src/handlers/createPets.ts b/samples/hono/src/handlers/createPets.ts index 8bd45e2c9..1e0995731 100644 --- a/samples/hono/src/handlers/createPets.ts +++ b/samples/hono/src/handlers/createPets.ts @@ -1,16 +1,12 @@ import { createFactory } from 'hono/factory'; import { zValidator } from '../petstore.validator'; import { CreatePetsContext } from '../petstore.context'; -import { createPetsBody, -createPetsResponse } from '../petstore.zod'; +import { createPetsBody, createPetsResponse } from '../petstore.zod'; const factory = createFactory(); - export const createPetsHandlers = factory.createHandlers( -zValidator('json', createPetsBody), -zValidator('response', createPetsResponse), -(c: CreatePetsContext) => { - - }, + zValidator('json', createPetsBody), + zValidator('response', createPetsResponse), + (c: CreatePetsContext) => {}, ); diff --git a/samples/hono/src/handlers/listPets.ts b/samples/hono/src/handlers/listPets.ts index f4888e732..aa64b9a3d 100644 --- a/samples/hono/src/handlers/listPets.ts +++ b/samples/hono/src/handlers/listPets.ts @@ -1,16 +1,12 @@ import { createFactory } from 'hono/factory'; import { zValidator } from '../petstore.validator'; import { ListPetsContext } from '../petstore.context'; -import { listPetsQueryParams, -listPetsResponse } from '../petstore.zod'; +import { listPetsQueryParams, listPetsResponse } from '../petstore.zod'; const factory = createFactory(); - export const listPetsHandlers = factory.createHandlers( -zValidator('query', listPetsQueryParams), -zValidator('response', listPetsResponse), -(c: ListPetsContext) => { - - }, + zValidator('query', listPetsQueryParams), + zValidator('response', listPetsResponse), + (c: ListPetsContext) => {}, ); diff --git a/samples/hono/src/handlers/showPetById.ts b/samples/hono/src/handlers/showPetById.ts index d88ede533..c54fea3c5 100644 --- a/samples/hono/src/handlers/showPetById.ts +++ b/samples/hono/src/handlers/showPetById.ts @@ -1,16 +1,12 @@ import { createFactory } from 'hono/factory'; import { zValidator } from '../petstore.validator'; import { ShowPetByIdContext } from '../petstore.context'; -import { showPetByIdParams, -showPetByIdResponse } from '../petstore.zod'; +import { showPetByIdParams, showPetByIdResponse } from '../petstore.zod'; const factory = createFactory(); - export const showPetByIdHandlers = factory.createHandlers( -zValidator('param', showPetByIdParams), -zValidator('response', showPetByIdResponse), -(c: ShowPetByIdContext) => { - - }, + zValidator('param', showPetByIdParams), + zValidator('response', showPetByIdResponse), + (c: ShowPetByIdContext) => {}, ); diff --git a/samples/hono/src/handlers/updatePets.ts b/samples/hono/src/handlers/updatePets.ts index 3d0a59d22..571e676f0 100644 --- a/samples/hono/src/handlers/updatePets.ts +++ b/samples/hono/src/handlers/updatePets.ts @@ -1,16 +1,12 @@ import { createFactory } from 'hono/factory'; import { zValidator } from '../petstore.validator'; import { UpdatePetsContext } from '../petstore.context'; -import { updatePetsBody, -updatePetsResponse } from '../petstore.zod'; +import { updatePetsBody, updatePetsResponse } from '../petstore.zod'; const factory = createFactory(); - export const updatePetsHandlers = factory.createHandlers( -zValidator('json', updatePetsBody), -zValidator('response', updatePetsResponse), -(c: UpdatePetsContext) => { - - }, + zValidator('json', updatePetsBody), + zValidator('response', updatePetsResponse), + (c: UpdatePetsContext) => {}, ); diff --git a/samples/hono/src/petstore.context.ts b/samples/hono/src/petstore.context.ts index 6ff83282f..b18332b0e 100644 --- a/samples/hono/src/petstore.context.ts +++ b/samples/hono/src/petstore.context.ts @@ -6,41 +6,59 @@ */ import type { Context, Env } from 'hono'; - type IsAny = 0 extends 1 & T ? true : false; -type IsUnknown = IsAny extends true ? false : unknown extends T ? true : false; +type IsUnknown = + IsAny extends true ? false : unknown extends T ? true : false; type Primitive = string | number | boolean | bigint | symbol | undefined | null; type isBuiltin = Primitive | Function | Date | Error | RegExp; type NonReadonly = T extends Exclude - ? T - : T extends Map - ? Map, NonReadonly> - : T extends ReadonlyMap - ? Map, NonReadonly> - : T extends WeakMap - ? WeakMap, NonReadonly> - : T extends Set - ? Set> - : T extends ReadonlySet - ? Set> - : T extends WeakSet - ? WeakSet> - : T extends Promise - ? Promise> - : T extends {} - ? { -readonly [Key in keyof T]: NonReadonly } - : IsUnknown extends true - ? unknown - : T; + ? T + : T extends Map + ? Map, NonReadonly> + : T extends ReadonlyMap + ? Map, NonReadonly> + : T extends WeakMap + ? WeakMap, NonReadonly> + : T extends Set + ? Set> + : T extends ReadonlySet + ? Set> + : T extends WeakSet + ? WeakSet> + : T extends Promise + ? Promise> + : T extends {} + ? { -readonly [Key in keyof T]: NonReadonly } + : IsUnknown extends true + ? unknown + : T; -import { ListPetsParams, -CreatePetsBodyItem, -Pet } from './petstore.schemas'; +import { ListPetsParams, CreatePetsBodyItem, Pet } from './petstore.schemas'; -export type ListPetsContext = Context -export type CreatePetsContext = Context -export type UpdatePetsContext = Context, }}> -export type ShowPetByIdContext = Context \ No newline at end of file +export type ListPetsContext = Context< + E, + '/pets', + { in: { query: ListPetsParams } } +>; +export type CreatePetsContext = Context< + E, + '/pets', + { in: { json: CreatePetsBodyItem[] } } +>; +export type UpdatePetsContext = Context< + E, + '/pets', + { in: { json: NonReadonly } } +>; +export type ShowPetByIdContext = Context< + E, + '/pets/:petId', + { + in: { + param: { + petId: string; + }; + }; + } +>; diff --git a/samples/hono/src/petstore.schemas.ts b/samples/hono/src/petstore.schemas.ts index 2602b0162..a1a1972c9 100644 --- a/samples/hono/src/petstore.schemas.ts +++ b/samples/hono/src/petstore.schemas.ts @@ -10,10 +10,10 @@ export type CreatePetsBodyItem = { }; export type ListPetsParams = { -/** - * How many items to return at one time (max 100) - */ -limit?: string; + /** + * How many items to return at one time (max 100) + */ + limit?: string; }; export interface Error { @@ -23,8 +23,7 @@ export interface Error { export type Pets = Pet[]; -export type CatType = typeof CatType[keyof typeof CatType]; - +export type CatType = (typeof CatType)[keyof typeof CatType]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const CatType = { @@ -36,8 +35,8 @@ export interface Cat { type: CatType; } -export type DachshundBreed = typeof DachshundBreed[keyof typeof DachshundBreed]; - +export type DachshundBreed = + (typeof DachshundBreed)[keyof typeof DachshundBreed]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const DachshundBreed = { @@ -49,8 +48,8 @@ export interface Dachshund { length: number; } -export type LabradoodleBreed = typeof LabradoodleBreed[keyof typeof LabradoodleBreed]; - +export type LabradoodleBreed = + (typeof LabradoodleBreed)[keyof typeof LabradoodleBreed]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const LabradoodleBreed = { @@ -62,33 +61,33 @@ export interface Labradoodle { cuteness: number; } -export type DogType = typeof DogType[keyof typeof DogType]; - +export type DogType = (typeof DogType)[keyof typeof DogType]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const DogType = { dog: 'dog', } as const; -export type Dog = (Labradoodle & { - barksPerMinute?: number; - type: DogType; -}) | (Dachshund & { - barksPerMinute?: number; - type: DogType; -}); - -export type PetCountry = typeof PetCountry[keyof typeof PetCountry]; +export type Dog = + | (Labradoodle & { + barksPerMinute?: number; + type: DogType; + }) + | (Dachshund & { + barksPerMinute?: number; + type: DogType; + }); +export type PetCountry = (typeof PetCountry)[keyof typeof PetCountry]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const PetCountry = { - 'People\'s_Republic_of_China': 'People\'s Republic of China', + "People's_Republic_of_China": "People's Republic of China", Uruguay: 'Uruguay', } as const; -export type PetCallingCode = typeof PetCallingCode[keyof typeof PetCallingCode]; - +export type PetCallingCode = + (typeof PetCallingCode)[keyof typeof PetCallingCode]; // eslint-disable-next-line @typescript-eslint/no-redeclare export const PetCallingCode = { @@ -96,21 +95,22 @@ export const PetCallingCode = { '+420': '+420', } as const; -export type Pet = (Dog & { - '@id'?: string; - callingCode?: PetCallingCode; - country?: PetCountry; - email?: string; - id: number; - name: string; - tag?: string; -}) | (Cat & { - '@id'?: string; - callingCode?: PetCallingCode; - country?: PetCountry; - email?: string; - id: number; - name: string; - tag?: string; -}); - +export type Pet = + | (Dog & { + '@id'?: string; + callingCode?: PetCallingCode; + country?: PetCountry; + email?: string; + id: number; + name: string; + tag?: string; + }) + | (Cat & { + '@id'?: string; + callingCode?: PetCallingCode; + country?: PetCountry; + email?: string; + id: number; + name: string; + tag?: string; + }); diff --git a/samples/hono/src/petstore.ts b/samples/hono/src/petstore.ts index f728ba264..69cd0e91a 100644 --- a/samples/hono/src/petstore.ts +++ b/samples/hono/src/petstore.ts @@ -4,44 +4,37 @@ * Swagger Petstore * OpenAPI spec version: 1.0.0 */ -import { - Hono -} from 'hono' +import { Hono } from 'hono'; import { listPetsHandlers } from './handlers/listPets'; import { createPetsHandlers } from './handlers/createPets'; import { updatePetsHandlers } from './handlers/updatePets'; import { showPetByIdHandlers } from './handlers/showPetById'; - -const app = new Hono() +const app = new Hono(); /** * @summary List all pets */ -app.get('/pets',...listPetsHandlers) - +app.get('/pets', ...listPetsHandlers); /** * @summary Create a pet */ -app.post('/pets',...createPetsHandlers) - +app.post('/pets', ...createPetsHandlers); /** * @summary Update a pet */ -app.put('/pets',...updatePetsHandlers) - +app.put('/pets', ...updatePetsHandlers); /** * @summary Info for a specific pet */ -app.get('/pets/:petId',...showPetByIdHandlers) - +app.get('/pets/:petId', ...showPetByIdHandlers); -export default app \ No newline at end of file +export default app; diff --git a/samples/hono/src/petstore.validator.ts b/samples/hono/src/petstore.validator.ts index 623ab34e6..44860da74 100644 --- a/samples/hono/src/petstore.validator.ts +++ b/samples/hono/src/petstore.validator.ts @@ -47,19 +47,19 @@ export const zValidator = [K in Target]?: K extends 'json' ? In : HasUndefined< - keyof ValidationTargetsWithResponse[K] - > extends true - ? { [K2 in keyof In]?: ValidationTargetsWithResponse[K][K2] } - : { [K2 in keyof In]: ValidationTargetsWithResponse[K][K2] }; + keyof ValidationTargetsWithResponse[K] + > extends true + ? { [K2 in keyof In]?: ValidationTargetsWithResponse[K][K2] } + : { [K2 in keyof In]: ValidationTargetsWithResponse[K][K2] }; } : { [K in Target]: K extends 'json' ? In : HasUndefined< - keyof ValidationTargetsWithResponse[K] - > extends true - ? { [K2 in keyof In]?: ValidationTargetsWithResponse[K][K2] } - : { [K2 in keyof In]: ValidationTargetsWithResponse[K][K2] }; + keyof ValidationTargetsWithResponse[K] + > extends true + ? { [K2 in keyof In]?: ValidationTargetsWithResponse[K][K2] } + : { [K2 in keyof In]: ValidationTargetsWithResponse[K][K2] }; }; out: { [K in Target]: Out }; }, diff --git a/samples/hono/src/petstore.zod.ts b/samples/hono/src/petstore.zod.ts index 1cecdeba4..ba81731de 100644 --- a/samples/hono/src/petstore.zod.ts +++ b/samples/hono/src/petstore.zod.ts @@ -7,71 +7,75 @@ import { z as zod } from 'zod'; export const listPetsQueryParams = zod.object({ - "limit": zod.string().optional() -}) - -export const listPetsResponseItem = zod.object({ - "@id": zod.string().optional(), - "id": zod.number().optional(), - "name": zod.string().optional(), - "tag": zod.string().optional(), - "email": zod.string().email().optional(), - "callingCode": zod.enum(['+33', '+420', '+33']).optional(), - "country": zod.enum(['People\'s Republic of China', 'Uruguay']).optional() -}) -export const listPetsResponse = zod.array(listPetsResponseItem) + limit: zod.string().optional(), +}); +export const listPetsResponseItem = zod + .object({ + '@id': zod.string().optional(), + id: zod.number().optional(), + name: zod.string().optional(), + tag: zod.string().optional(), + email: zod.string().email().optional(), + callingCode: zod.enum(['+33', '+420', '+33']).optional(), + country: zod.enum(["People's Republic of China", 'Uruguay']).optional(), + }) + .strict(); +export const listPetsResponse = zod.array(listPetsResponseItem); export const createPetsBodyItem = zod.object({ - "name": zod.string().optional(), - "tag": zod.string().optional() -}) -export const createPetsBody = zod.array(createPetsBodyItem) - -export const createPetsResponse = zod.object({ - "@id": zod.string().optional(), - "id": zod.number(), - "name": zod.string(), - "tag": zod.string().optional(), - "email": zod.string().email().optional(), - "callingCode": zod.enum(['+33', '+420', '+33']).optional(), - "country": zod.enum(['People\'s Republic of China', 'Uruguay']).optional() -}) + name: zod.string().optional(), + tag: zod.string().optional(), +}); +export const createPetsBody = zod.array(createPetsBodyItem); +export const createPetsResponse = zod + .object({ + '@id': zod.string().optional(), + id: zod.number(), + name: zod.string(), + tag: zod.string().optional(), + email: zod.string().email().optional(), + callingCode: zod.enum(['+33', '+420', '+33']).optional(), + country: zod.enum(["People's Republic of China", 'Uruguay']).optional(), + }) + .strict(); export const updatePetsBody = zod.object({ - "@id": zod.string().optional(), - "id": zod.number(), - "name": zod.string(), - "tag": zod.string().optional(), - "email": zod.string().email().optional(), - "callingCode": zod.enum(['+33', '+420', '+33']).optional(), - "country": zod.enum(['People\'s Republic of China', 'Uruguay']).optional() -}) - -export const updatePetsResponse = zod.object({ - "@id": zod.string().optional(), - "id": zod.number(), - "name": zod.string(), - "tag": zod.string().optional(), - "email": zod.string().email().optional(), - "callingCode": zod.enum(['+33', '+420', '+33']).optional(), - "country": zod.enum(['People\'s Republic of China', 'Uruguay']).optional() -}) + '@id': zod.string().optional(), + id: zod.number(), + name: zod.string(), + tag: zod.string().optional(), + email: zod.string().email().optional(), + callingCode: zod.enum(['+33', '+420', '+33']).optional(), + country: zod.enum(["People's Republic of China", 'Uruguay']).optional(), +}); +export const updatePetsResponse = zod + .object({ + '@id': zod.string().optional(), + id: zod.number(), + name: zod.string(), + tag: zod.string().optional(), + email: zod.string().email().optional(), + callingCode: zod.enum(['+33', '+420', '+33']).optional(), + country: zod.enum(["People's Republic of China", 'Uruguay']).optional(), + }) + .strict(); export const showPetByIdParams = zod.object({ - "petId": zod.string(), - "testId": zod.string() -}) - -export const showPetByIdResponse = zod.object({ - "@id": zod.string().optional(), - "id": zod.number(), - "name": zod.string(), - "tag": zod.string().optional(), - "email": zod.string().email().optional(), - "callingCode": zod.enum(['+33', '+420', '+33']).optional(), - "country": zod.enum(['People\'s Republic of China', 'Uruguay']).optional() -}) + petId: zod.string(), + testId: zod.string(), +}); +export const showPetByIdResponse = zod + .object({ + '@id': zod.string().optional(), + id: zod.number(), + name: zod.string(), + tag: zod.string().optional(), + email: zod.string().email().optional(), + callingCode: zod.enum(['+33', '+420', '+33']).optional(), + country: zod.enum(["People's Republic of China", 'Uruguay']).optional(), + }) + .strict(); diff --git a/tests/configs/zod.config.ts b/tests/configs/zod.config.ts index 66d400bfd..9a1c64611 100644 --- a/tests/configs/zod.config.ts +++ b/tests/configs/zod.config.ts @@ -69,4 +69,24 @@ export default defineConfig({ target: '../specifications/arrays.yaml', }, }, + strictMode: { + output: { + target: '../generated/zod/strict-mode.ts', + client: 'zod', + override: { + zod: { + strict: { + response: true, + query: true, + header: true, + param: true, + body: true, + }, + }, + }, + }, + input: { + target: '../specifications/circular.yaml', + }, + }, });