@kubb/plugin-zod 5.0.0-alpha.24 → 5.0.0-alpha.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1673 -88
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +317 -2
- package/dist/index.js +1646 -88
- package/dist/index.js.map +1 -1
- package/package.json +5 -33
- package/src/components/Operations.tsx +22 -15
- package/src/components/Zod.tsx +18 -118
- package/src/components/ZodMini.tsx +41 -0
- package/src/constants.ts +5 -0
- package/src/generators/zodGenerator.tsx +165 -158
- package/src/generators/zodGeneratorLegacy.tsx +401 -0
- package/src/index.ts +11 -1
- package/src/plugin.ts +105 -129
- package/src/presets.ts +25 -0
- package/src/printers/printerZod.ts +271 -0
- package/src/printers/printerZodMini.ts +246 -0
- package/src/resolvers/resolverZod.ts +71 -0
- package/src/resolvers/resolverZodLegacy.ts +60 -0
- package/src/types.ts +121 -92
- package/src/utils.ts +248 -0
- package/dist/components-DW4Q2yVq.js +0 -868
- package/dist/components-DW4Q2yVq.js.map +0 -1
- package/dist/components-qFUGs0vU.cjs +0 -916
- package/dist/components-qFUGs0vU.cjs.map +0 -1
- package/dist/components.cjs +0 -4
- package/dist/components.d.ts +0 -56
- package/dist/components.js +0 -2
- package/dist/generators-C9BCTLXg.cjs +0 -301
- package/dist/generators-C9BCTLXg.cjs.map +0 -1
- package/dist/generators-maqx12yN.js +0 -290
- package/dist/generators-maqx12yN.js.map +0 -1
- package/dist/generators.cjs +0 -4
- package/dist/generators.d.ts +0 -12
- package/dist/generators.js +0 -2
- package/dist/templates/ToZod.source.cjs +0 -7
- package/dist/templates/ToZod.source.cjs.map +0 -1
- package/dist/templates/ToZod.source.d.ts +0 -7
- package/dist/templates/ToZod.source.js +0 -6
- package/dist/templates/ToZod.source.js.map +0 -1
- package/dist/types-CClg-ikj.d.ts +0 -172
- package/src/components/index.ts +0 -2
- package/src/generators/index.ts +0 -2
- package/src/generators/operationsGenerator.tsx +0 -50
- package/src/parser.ts +0 -952
- package/src/templates/ToZod.source.ts +0 -4
- package/templates/ToZod.ts +0 -61
package/src/types.ts
CHANGED
|
@@ -1,18 +1,90 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
|
|
1
|
+
import type { OperationParamsResolver } from '@kubb/ast'
|
|
2
|
+
import type { OperationNode, ParameterNode, SchemaNode, StatusCode, Visitor } from '@kubb/ast/types'
|
|
3
|
+
import type {
|
|
4
|
+
CompatibilityPreset,
|
|
5
|
+
Exclude,
|
|
6
|
+
Generator,
|
|
7
|
+
Group,
|
|
8
|
+
Include,
|
|
9
|
+
Output,
|
|
10
|
+
Override,
|
|
11
|
+
PluginFactoryOptions,
|
|
12
|
+
ResolvePathOptions,
|
|
13
|
+
Resolver,
|
|
14
|
+
UserGroup,
|
|
15
|
+
} from '@kubb/core'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The concrete resolver type for `@kubb/plugin-zod`.
|
|
19
|
+
* Extends the base `Resolver` with zod-specific naming helpers.
|
|
20
|
+
*/
|
|
21
|
+
export type ResolverZod = Resolver &
|
|
22
|
+
OperationParamsResolver & {
|
|
23
|
+
/**
|
|
24
|
+
* Resolves a camelCase schema function name with a `Schema` suffix.
|
|
25
|
+
*/
|
|
26
|
+
resolveName(name: string): string
|
|
27
|
+
/**
|
|
28
|
+
* Resolves the name for a `z.infer<typeof ...>` type export from an already-resolved function name.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* resolver.resolveInferName('petSchema') // → 'PetSchema'
|
|
32
|
+
* resolver.resolveInferName('addPet200Schema') // → 'AddPet200Schema'
|
|
33
|
+
*/
|
|
34
|
+
resolveInferName(name: string): string
|
|
35
|
+
/**
|
|
36
|
+
* Resolves a PascalCase path/file name for the generated output.
|
|
37
|
+
*/
|
|
38
|
+
resolvePathName(name: string, type?: 'file' | 'function' | 'type' | 'const'): string
|
|
39
|
+
/**
|
|
40
|
+
* Resolves the name for an operation response by status code.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* resolver.resolveResponseStatusName(node, 200) // → 'listPetsStatus200Schema'
|
|
44
|
+
*/
|
|
45
|
+
resolveResponseStatusName(node: OperationNode, statusCode: StatusCode): string
|
|
46
|
+
/**
|
|
47
|
+
* Resolves the name for the collection of all operation responses.
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* resolver.resolveResponsesName(node) // → 'listPetsResponsesSchema'
|
|
51
|
+
*/
|
|
52
|
+
resolveResponsesName(node: OperationNode): string
|
|
53
|
+
/**
|
|
54
|
+
* Resolves the name for the union of all operation responses.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* resolver.resolveResponseName(node) // → 'listPetsResponseSchema'
|
|
58
|
+
*/
|
|
59
|
+
resolveResponseName(node: OperationNode): string
|
|
60
|
+
/**
|
|
61
|
+
* Resolves the name for an operation's grouped path parameters type.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* resolver.resolvePathParamsName(node, param) // → 'deletePetPathPetIdSchema'
|
|
65
|
+
*/
|
|
66
|
+
resolvePathParamsName(node: OperationNode, param: ParameterNode): string
|
|
67
|
+
/**
|
|
68
|
+
* Resolves the name for an operation's grouped query parameters type.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* resolver.resolveQueryParamsName(node, param) // → 'findPetsByStatusQueryStatusSchema'
|
|
72
|
+
*/
|
|
73
|
+
resolveQueryParamsName(node: OperationNode, param: ParameterNode): string
|
|
74
|
+
/**
|
|
75
|
+
* Resolves the name for an operation's grouped header parameters type.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* resolver.resolveHeaderParamsName(node, param) // → 'deletePetHeaderApiKeySchema'
|
|
79
|
+
*/
|
|
80
|
+
resolveHeaderParamsName(node: OperationNode, param: ParameterNode): string
|
|
81
|
+
}
|
|
5
82
|
|
|
6
83
|
export type Options = {
|
|
7
84
|
/**
|
|
8
85
|
* @default 'zod'
|
|
9
86
|
*/
|
|
10
|
-
output?: Output
|
|
11
|
-
/**
|
|
12
|
-
* Define which contentType should be used.
|
|
13
|
-
* By default, the first JSON valid mediaType is used
|
|
14
|
-
*/
|
|
15
|
-
contentType?: contentType
|
|
87
|
+
output?: Output
|
|
16
88
|
/**
|
|
17
89
|
* Group the Zod schemas based on the provided name.
|
|
18
90
|
*/
|
|
@@ -36,8 +108,7 @@ export type Options = {
|
|
|
36
108
|
* Path is used as-is; relative paths are based on the generated file location.
|
|
37
109
|
* @default 'zod'
|
|
38
110
|
*/
|
|
39
|
-
importPath?: string
|
|
40
|
-
|
|
111
|
+
importPath?: 'zod' | 'zod/mini' | (string & {})
|
|
41
112
|
/**
|
|
42
113
|
* Choose to use date or datetime as JavaScript Date instead of string.
|
|
43
114
|
* - false falls back to a simple z.string() format.
|
|
@@ -46,33 +117,8 @@ export type Options = {
|
|
|
46
117
|
* - 'stringLocal' uses z.string().datetime({ local: true }) for local datetime validation.
|
|
47
118
|
* - 'date' uses z.date() for JavaScript Date objects.
|
|
48
119
|
* @default 'string'
|
|
49
|
-
* @note 'stringOffset' will become the default in Kubb v3.
|
|
50
120
|
*/
|
|
51
121
|
dateType?: false | 'string' | 'stringOffset' | 'stringLocal' | 'date'
|
|
52
|
-
/**
|
|
53
|
-
* Choose to use `number` or `bigint` for integer fields with `int64` format.
|
|
54
|
-
* - 'number' uses the JavaScript `number` type (matches JSON.parse() runtime behavior).
|
|
55
|
-
* - 'bigint' uses the JavaScript `bigint` type (accurate for values exceeding Number.MAX_SAFE_INTEGER).
|
|
56
|
-
* @note in v5 of Kubb 'bigint' will become the default to better align with OpenAPI's int64 specification.
|
|
57
|
-
* @default 'number'
|
|
58
|
-
*/
|
|
59
|
-
integerType?: 'number' | 'bigint'
|
|
60
|
-
/**
|
|
61
|
-
* Which type to use when the Swagger/OpenAPI file is not providing more information.
|
|
62
|
-
* - 'any' allows any value.
|
|
63
|
-
* - 'unknown' requires type narrowing before use.
|
|
64
|
-
* - 'void' represents no value.
|
|
65
|
-
* @default 'any'
|
|
66
|
-
*/
|
|
67
|
-
unknownType?: 'any' | 'unknown' | 'void'
|
|
68
|
-
/**
|
|
69
|
-
* Which type to use for empty schema values.
|
|
70
|
-
* - 'any' allows any value.
|
|
71
|
-
* - 'unknown' requires type narrowing before use.
|
|
72
|
-
* - 'void' represents no value.
|
|
73
|
-
* @default `unknownType`
|
|
74
|
-
*/
|
|
75
|
-
emptySchemaType?: 'any' | 'unknown' | 'void'
|
|
76
122
|
/**
|
|
77
123
|
* Use TypeScript(`@kubb/plugin-ts`) to add type annotation.
|
|
78
124
|
*/
|
|
@@ -82,91 +128,74 @@ export type Options = {
|
|
|
82
128
|
*/
|
|
83
129
|
inferred?: boolean
|
|
84
130
|
/**
|
|
85
|
-
* Use of z.coerce.string() instead of z.string()
|
|
86
|
-
*
|
|
131
|
+
* Use of z.coerce.string() instead of z.string().
|
|
132
|
+
* Can also be an object to enable coercion for dates, strings, and numbers.
|
|
87
133
|
*/
|
|
88
|
-
coercion?:
|
|
89
|
-
| boolean
|
|
90
|
-
| {
|
|
91
|
-
dates?: boolean
|
|
92
|
-
strings?: boolean
|
|
93
|
-
numbers?: boolean
|
|
94
|
-
}
|
|
95
|
-
operations?: boolean
|
|
96
|
-
mapper?: Record<string, string>
|
|
97
|
-
transformers?: {
|
|
98
|
-
/**
|
|
99
|
-
* Customize the names based on the type that is provided by the plugin.
|
|
100
|
-
*/
|
|
101
|
-
name?: (name: ResolveNameParams['name'], type?: ResolveNameParams['type']) => string
|
|
102
|
-
/**
|
|
103
|
-
* Receive schema and baseName(propertyName) and return FakerMeta array
|
|
104
|
-
* TODO TODO add docs
|
|
105
|
-
* @beta
|
|
106
|
-
*/
|
|
107
|
-
schema?: (
|
|
108
|
-
props: {
|
|
109
|
-
schema: SchemaObject | null
|
|
110
|
-
name: string | null
|
|
111
|
-
parentName: string | null
|
|
112
|
-
},
|
|
113
|
-
defaultSchemas: Schema[],
|
|
114
|
-
) => Schema[] | undefined
|
|
115
|
-
}
|
|
134
|
+
coercion?: boolean | { dates?: boolean; strings?: boolean; numbers?: boolean }
|
|
116
135
|
/**
|
|
117
|
-
*
|
|
118
|
-
* - '3' uses Zod v3.x syntax and features.
|
|
119
|
-
* - '4' uses Zod v4.x syntax and features.
|
|
120
|
-
* @default '3'
|
|
136
|
+
* Generate operation-level schemas (grouped by operationId).
|
|
121
137
|
*/
|
|
122
|
-
|
|
138
|
+
operations?: boolean
|
|
123
139
|
/**
|
|
124
140
|
* Which Zod GUID validator to use for OpenAPI `format: uuid`.
|
|
125
141
|
* - 'uuid' uses UUID validation.
|
|
126
|
-
* - 'guid' uses GUID validation
|
|
142
|
+
* - 'guid' uses GUID validation.
|
|
127
143
|
* @default 'uuid'
|
|
128
144
|
*/
|
|
129
145
|
guidType?: 'uuid' | 'guid'
|
|
130
146
|
/**
|
|
131
147
|
* Use Zod Mini's functional API for better tree-shaking support.
|
|
132
|
-
* When enabled, generates functional syntax (e.g., `z.optional(z.string())`)
|
|
133
|
-
*
|
|
148
|
+
* When enabled, generates functional syntax (e.g., `z.optional(z.string())`)
|
|
149
|
+
* instead of chainable methods (e.g., `z.string().optional()`).
|
|
150
|
+
* When `mini: true`, `importPath` will default to 'zod/mini'.
|
|
134
151
|
* @default false
|
|
135
152
|
*/
|
|
136
153
|
mini?: boolean
|
|
137
154
|
/**
|
|
138
|
-
* Callback function to wrap the output of the generated zod schema
|
|
155
|
+
* Callback function to wrap the output of the generated zod schema.
|
|
139
156
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
|
|
157
|
+
* Useful for edge cases like adding `.openapi()` metadata or wrapping
|
|
158
|
+
* schemas with extension helpers (openapi -> zod -> openapi round-trips).
|
|
159
|
+
*/
|
|
160
|
+
wrapOutput?: (arg: { output: string; schema: SchemaNode }) => string | undefined
|
|
161
|
+
/**
|
|
162
|
+
* How to style your params, by default no casing is applied
|
|
163
|
+
* - 'camelcase' uses camelCase for pathParams, queryParams and headerParams property names
|
|
164
|
+
* @default undefined
|
|
143
165
|
*/
|
|
144
|
-
|
|
166
|
+
paramsCasing?: 'camelcase'
|
|
145
167
|
/**
|
|
146
|
-
* Define
|
|
168
|
+
* Define additional generators next to the zod generators.
|
|
147
169
|
*/
|
|
148
170
|
generators?: Array<Generator<PluginZod>>
|
|
171
|
+
/**
|
|
172
|
+
* Compatibility preset to ease migration from previous Kubb versions.
|
|
173
|
+
*/
|
|
174
|
+
compatibilityPreset?: CompatibilityPreset
|
|
175
|
+
/**
|
|
176
|
+
* Custom resolver instances for zod-specific name resolution.
|
|
177
|
+
*/
|
|
178
|
+
resolvers?: Array<ResolverZod>
|
|
179
|
+
/**
|
|
180
|
+
* AST visitor transformers applied during code generation.
|
|
181
|
+
*/
|
|
182
|
+
transformers?: Array<Visitor>
|
|
149
183
|
}
|
|
150
184
|
|
|
151
185
|
type ResolvedOptions = {
|
|
152
|
-
output: Output
|
|
153
|
-
group:
|
|
154
|
-
override: NonNullable<Options['override']>
|
|
155
|
-
transformers: NonNullable<Options['transformers']>
|
|
186
|
+
output: Output
|
|
187
|
+
group: Group | undefined
|
|
156
188
|
dateType: NonNullable<Options['dateType']>
|
|
157
|
-
integerType: NonNullable<Options['integerType']>
|
|
158
|
-
unknownType: NonNullable<Options['unknownType']>
|
|
159
|
-
emptySchemaType: NonNullable<Options['emptySchemaType']>
|
|
160
189
|
typed: NonNullable<Options['typed']>
|
|
161
190
|
inferred: NonNullable<Options['inferred']>
|
|
162
|
-
mapper: NonNullable<Options['mapper']>
|
|
163
191
|
importPath: NonNullable<Options['importPath']>
|
|
164
192
|
coercion: NonNullable<Options['coercion']>
|
|
165
193
|
operations: NonNullable<Options['operations']>
|
|
166
|
-
wrapOutput: Options['wrapOutput']
|
|
167
|
-
version: NonNullable<Options['version']>
|
|
168
194
|
guidType: NonNullable<Options['guidType']>
|
|
169
195
|
mini: NonNullable<Options['mini']>
|
|
196
|
+
wrapOutput: Options['wrapOutput']
|
|
197
|
+
paramsCasing: Options['paramsCasing']
|
|
198
|
+
transformers: Array<Visitor>
|
|
170
199
|
}
|
|
171
200
|
|
|
172
|
-
export type PluginZod = PluginFactoryOptions<'plugin-zod', Options, ResolvedOptions, never, ResolvePathOptions>
|
|
201
|
+
export type PluginZod = PluginFactoryOptions<'plugin-zod', Options, ResolvedOptions, never, ResolvePathOptions, ResolverZod>
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { stringify, toRegExpString } from '@internals/utils'
|
|
2
|
+
import { createProperty, createSchema, extractRefName } from '@kubb/ast'
|
|
3
|
+
import type { OperationNode, ParameterNode, SchemaNode } from '@kubb/ast/types'
|
|
4
|
+
import type { PluginZod, ResolverZod } from './types.ts'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Returns `true` when the given coercion option enables coercion for the specified type.
|
|
8
|
+
*/
|
|
9
|
+
export function shouldCoerce(coercion: PluginZod['resolvedOptions']['coercion'] | undefined, type: 'dates' | 'strings' | 'numbers'): boolean {
|
|
10
|
+
if (coercion === undefined || coercion === false) return false
|
|
11
|
+
if (coercion === true) return true
|
|
12
|
+
|
|
13
|
+
return !!coercion[type]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Collects all resolved schema names for an operation's parameters and responses
|
|
18
|
+
* into a single lookup object, useful for building imports and type references.
|
|
19
|
+
*/
|
|
20
|
+
export function buildSchemaNames(node: OperationNode, { params, resolver }: { params: Array<ParameterNode>; resolver: ResolverZod }) {
|
|
21
|
+
const pathParam = params.find((p) => p.in === 'path')
|
|
22
|
+
const queryParam = params.find((p) => p.in === 'query')
|
|
23
|
+
const headerParam = params.find((p) => p.in === 'header')
|
|
24
|
+
|
|
25
|
+
const responses: Record<number | string, string> = {}
|
|
26
|
+
const errors: Record<number | string, string> = {}
|
|
27
|
+
|
|
28
|
+
for (const res of node.responses) {
|
|
29
|
+
const name = resolver.resolveResponseStatusName(node, res.statusCode)
|
|
30
|
+
const statusNum = Number(res.statusCode)
|
|
31
|
+
|
|
32
|
+
if (!Number.isNaN(statusNum)) {
|
|
33
|
+
responses[statusNum] = name
|
|
34
|
+
if (statusNum >= 400) {
|
|
35
|
+
errors[statusNum] = name
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
responses['default'] = resolver.resolveResponseName(node)
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
request: node.requestBody?.schema ? resolver.resolveDataName(node) : undefined,
|
|
44
|
+
parameters: {
|
|
45
|
+
path: pathParam ? resolver.resolvePathParamsName(node, pathParam) : undefined,
|
|
46
|
+
query: queryParam ? resolver.resolveQueryParamsName(node, queryParam) : undefined,
|
|
47
|
+
header: headerParam ? resolver.resolveHeaderParamsName(node, headerParam) : undefined,
|
|
48
|
+
},
|
|
49
|
+
responses,
|
|
50
|
+
errors,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Format a default value as a code-level literal.
|
|
56
|
+
* Objects become `{}`, primitives become their string representation, strings are quoted.
|
|
57
|
+
*/
|
|
58
|
+
export function formatDefault(value: unknown): string {
|
|
59
|
+
if (typeof value === 'string') return stringify(value)
|
|
60
|
+
if (typeof value === 'object' && value !== null) return '{}'
|
|
61
|
+
|
|
62
|
+
return String(value ?? '')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Format a primitive enum/literal value.
|
|
67
|
+
* Strings are quoted; numbers and booleans are emitted raw.
|
|
68
|
+
*/
|
|
69
|
+
export function formatLiteral(v: string | number | boolean): string {
|
|
70
|
+
if (typeof v === 'string') return stringify(v)
|
|
71
|
+
|
|
72
|
+
return String(v)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export type NumericConstraints = {
|
|
76
|
+
min?: number
|
|
77
|
+
max?: number
|
|
78
|
+
exclusiveMinimum?: number
|
|
79
|
+
exclusiveMaximum?: number
|
|
80
|
+
multipleOf?: number
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export type LengthConstraints = {
|
|
84
|
+
min?: number
|
|
85
|
+
max?: number
|
|
86
|
+
pattern?: string
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export type ModifierOptions = {
|
|
90
|
+
value: string
|
|
91
|
+
nullable?: boolean
|
|
92
|
+
optional?: boolean
|
|
93
|
+
nullish?: boolean
|
|
94
|
+
defaultValue?: unknown
|
|
95
|
+
description?: string
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Build `.min()` / `.max()` / `.gt()` / `.lt()` constraint chains for numbers
|
|
100
|
+
* using the standard chainable Zod v4 API.
|
|
101
|
+
*/
|
|
102
|
+
export function numberConstraints({ min, max, exclusiveMinimum, exclusiveMaximum, multipleOf }: NumericConstraints): string {
|
|
103
|
+
return [
|
|
104
|
+
min !== undefined ? `.min(${min})` : '',
|
|
105
|
+
max !== undefined ? `.max(${max})` : '',
|
|
106
|
+
exclusiveMinimum !== undefined ? `.gt(${exclusiveMinimum})` : '',
|
|
107
|
+
exclusiveMaximum !== undefined ? `.lt(${exclusiveMaximum})` : '',
|
|
108
|
+
multipleOf !== undefined ? `.multipleOf(${multipleOf})` : '',
|
|
109
|
+
].join('')
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Build `.min()` / `.max()` / `.regex()` chains for strings/arrays
|
|
114
|
+
* using the standard chainable Zod v4 API.
|
|
115
|
+
*/
|
|
116
|
+
export function lengthConstraints({ min, max, pattern }: LengthConstraints): string {
|
|
117
|
+
return [
|
|
118
|
+
min !== undefined ? `.min(${min})` : '',
|
|
119
|
+
max !== undefined ? `.max(${max})` : '',
|
|
120
|
+
pattern !== undefined ? `.regex(${toRegExpString(pattern, null)})` : '',
|
|
121
|
+
].join('')
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Build `.check(z.minimum(), z.maximum())` for `zod/mini` numeric constraints.
|
|
126
|
+
*/
|
|
127
|
+
export function numberChecksMini({ min, max, exclusiveMinimum, exclusiveMaximum, multipleOf }: NumericConstraints): string {
|
|
128
|
+
const checks: string[] = []
|
|
129
|
+
if (min !== undefined) checks.push(`z.minimum(${min})`)
|
|
130
|
+
if (max !== undefined) checks.push(`z.maximum(${max})`)
|
|
131
|
+
if (exclusiveMinimum !== undefined) checks.push(`z.minimum(${exclusiveMinimum}, { exclusive: true })`)
|
|
132
|
+
if (exclusiveMaximum !== undefined) checks.push(`z.maximum(${exclusiveMaximum}, { exclusive: true })`)
|
|
133
|
+
if (multipleOf !== undefined) checks.push(`z.multipleOf(${multipleOf})`)
|
|
134
|
+
return checks.length ? `.check(${checks.join(', ')})` : ''
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Build `.check(z.minLength(), z.maxLength(), z.regex())` for `zod/mini` length constraints.
|
|
139
|
+
*/
|
|
140
|
+
export function lengthChecksMini({ min, max, pattern }: LengthConstraints): string {
|
|
141
|
+
const checks: string[] = []
|
|
142
|
+
if (min !== undefined) checks.push(`z.minLength(${min})`)
|
|
143
|
+
if (max !== undefined) checks.push(`z.maxLength(${max})`)
|
|
144
|
+
if (pattern !== undefined) checks.push(`z.regex(${toRegExpString(pattern, null)})`)
|
|
145
|
+
return checks.length ? `.check(${checks.join(', ')})` : ''
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Apply nullable / optional / nullish modifiers and an optional `.describe()` call
|
|
150
|
+
* to a schema value string using the chainable Zod v4 API.
|
|
151
|
+
*/
|
|
152
|
+
export function applyModifiers({ value, nullable, optional, nullish, defaultValue, description }: ModifierOptions): string {
|
|
153
|
+
let result = value
|
|
154
|
+
if (nullish || (nullable && optional)) {
|
|
155
|
+
result = `${result}.nullish()`
|
|
156
|
+
} else if (optional) {
|
|
157
|
+
result = `${result}.optional()`
|
|
158
|
+
} else if (nullable) {
|
|
159
|
+
result = `${result}.nullable()`
|
|
160
|
+
}
|
|
161
|
+
if (defaultValue !== undefined) {
|
|
162
|
+
result = `${result}.default(${formatDefault(defaultValue)})`
|
|
163
|
+
}
|
|
164
|
+
if (description) {
|
|
165
|
+
result = `${result}.describe(${stringify(description)})`
|
|
166
|
+
}
|
|
167
|
+
return result
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Apply nullable / optional / nullish modifiers using the functional `zod/mini` API
|
|
172
|
+
* (`z.nullable()`, `z.optional()`, `z.nullish()`).
|
|
173
|
+
*/
|
|
174
|
+
export function applyMiniModifiers({ value, nullable, optional, nullish, defaultValue }: Omit<ModifierOptions, 'description'>): string {
|
|
175
|
+
let result = value
|
|
176
|
+
if (nullish) {
|
|
177
|
+
result = `z.nullish(${result})`
|
|
178
|
+
} else {
|
|
179
|
+
if (nullable) {
|
|
180
|
+
result = `z.nullable(${result})`
|
|
181
|
+
}
|
|
182
|
+
if (optional) {
|
|
183
|
+
result = `z.optional(${result})`
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (defaultValue !== undefined) {
|
|
187
|
+
result = `z._default(${result}, ${formatDefault(defaultValue)})`
|
|
188
|
+
}
|
|
189
|
+
return result
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Returns true when the schema tree contains a self-referential `$ref`
|
|
194
|
+
* whose resolved name matches `schemaName`.
|
|
195
|
+
*
|
|
196
|
+
* A `visited` set prevents infinite recursion on circular schema graphs.
|
|
197
|
+
*/
|
|
198
|
+
export function containsSelfRef(
|
|
199
|
+
node: SchemaNode,
|
|
200
|
+
{ schemaName, resolver, visited = new Set() }: { schemaName: string; resolver: ResolverZod | undefined; visited?: Set<SchemaNode> },
|
|
201
|
+
): boolean {
|
|
202
|
+
if (visited.has(node)) return false
|
|
203
|
+
visited.add(node)
|
|
204
|
+
|
|
205
|
+
if (node.type === 'ref' && node.ref) {
|
|
206
|
+
const rawName = extractRefName(node.ref) ?? node.name
|
|
207
|
+
const resolved = rawName ? (resolver?.default(rawName, 'function') ?? rawName) : node.name
|
|
208
|
+
return resolved === schemaName
|
|
209
|
+
}
|
|
210
|
+
if (node.type === 'object') {
|
|
211
|
+
if (node.properties?.some((p) => containsSelfRef(p.schema, { schemaName, resolver, visited }))) return true
|
|
212
|
+
if (node.additionalProperties && node.additionalProperties !== true) {
|
|
213
|
+
return containsSelfRef(node.additionalProperties, { schemaName, resolver, visited })
|
|
214
|
+
}
|
|
215
|
+
return false
|
|
216
|
+
}
|
|
217
|
+
if (node.type === 'array' || node.type === 'tuple') {
|
|
218
|
+
return node.items?.some((item) => containsSelfRef(item, { schemaName, resolver, visited })) ?? false
|
|
219
|
+
}
|
|
220
|
+
if (node.type === 'union' || node.type === 'intersection') {
|
|
221
|
+
return node.members?.some((m) => containsSelfRef(m, { schemaName, resolver, visited })) ?? false
|
|
222
|
+
}
|
|
223
|
+
return false
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
type BuildGroupedParamsSchemaOptions = {
|
|
227
|
+
params: Array<ParameterNode>
|
|
228
|
+
optional?: boolean
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Builds an `object` schema node grouping the given parameter nodes.
|
|
233
|
+
* The `primitive: 'object'` marker ensures the Zod printer emits `z.object(…)` rather than a record.
|
|
234
|
+
*/
|
|
235
|
+
export function buildGroupedParamsSchema({ params, optional }: BuildGroupedParamsSchemaOptions): SchemaNode {
|
|
236
|
+
return createSchema({
|
|
237
|
+
type: 'object',
|
|
238
|
+
optional,
|
|
239
|
+
primitive: 'object',
|
|
240
|
+
properties: params.map((param) =>
|
|
241
|
+
createProperty({
|
|
242
|
+
name: param.name,
|
|
243
|
+
required: param.required,
|
|
244
|
+
schema: param.schema,
|
|
245
|
+
}),
|
|
246
|
+
),
|
|
247
|
+
})
|
|
248
|
+
}
|