@kubb/plugin-zod 5.9.0-canary.20261111T122006 → 5.9.0
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 +760 -806
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +761 -807
- package/dist/index.js.map +1 -1
- package/docs/guides/customization.md +31 -23
- package/docs/options.md +2 -1
- package/kubb-extension.json +1 -1
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -5,143 +5,407 @@ Object.defineProperties(exports, {
|
|
|
5
5
|
let _kubb_kit = require("@kubb/kit");
|
|
6
6
|
let _kubb_renderer_jsx = require("@kubb/renderer-jsx");
|
|
7
7
|
let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
|
|
8
|
-
// ../../internals/
|
|
8
|
+
// ../../internals/utils/src/reserved.ts
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
* JavaScript and Java reserved words.
|
|
11
|
+
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
12
|
+
*/
|
|
13
|
+
const reservedWords = /* @__PURE__ */ new Set([
|
|
14
|
+
"abstract",
|
|
15
|
+
"arguments",
|
|
16
|
+
"await",
|
|
17
|
+
"boolean",
|
|
18
|
+
"break",
|
|
19
|
+
"byte",
|
|
20
|
+
"case",
|
|
21
|
+
"catch",
|
|
22
|
+
"char",
|
|
23
|
+
"class",
|
|
24
|
+
"const",
|
|
25
|
+
"continue",
|
|
26
|
+
"debugger",
|
|
27
|
+
"default",
|
|
28
|
+
"delete",
|
|
29
|
+
"do",
|
|
30
|
+
"double",
|
|
31
|
+
"else",
|
|
32
|
+
"enum",
|
|
33
|
+
"eval",
|
|
34
|
+
"export",
|
|
35
|
+
"extends",
|
|
36
|
+
"false",
|
|
37
|
+
"final",
|
|
38
|
+
"finally",
|
|
39
|
+
"float",
|
|
40
|
+
"for",
|
|
41
|
+
"function",
|
|
42
|
+
"goto",
|
|
43
|
+
"if",
|
|
44
|
+
"implements",
|
|
45
|
+
"import",
|
|
46
|
+
"in",
|
|
47
|
+
"instanceof",
|
|
48
|
+
"int",
|
|
49
|
+
"interface",
|
|
50
|
+
"let",
|
|
51
|
+
"long",
|
|
52
|
+
"native",
|
|
53
|
+
"new",
|
|
54
|
+
"null",
|
|
55
|
+
"package",
|
|
56
|
+
"private",
|
|
57
|
+
"protected",
|
|
58
|
+
"public",
|
|
59
|
+
"return",
|
|
60
|
+
"short",
|
|
61
|
+
"static",
|
|
62
|
+
"super",
|
|
63
|
+
"switch",
|
|
64
|
+
"synchronized",
|
|
65
|
+
"this",
|
|
66
|
+
"throw",
|
|
67
|
+
"throws",
|
|
68
|
+
"transient",
|
|
69
|
+
"true",
|
|
70
|
+
"try",
|
|
71
|
+
"typeof",
|
|
72
|
+
"var",
|
|
73
|
+
"void",
|
|
74
|
+
"volatile",
|
|
75
|
+
"while",
|
|
76
|
+
"with",
|
|
77
|
+
"yield",
|
|
78
|
+
"Array",
|
|
79
|
+
"Date",
|
|
80
|
+
"hasOwnProperty",
|
|
81
|
+
"Infinity",
|
|
82
|
+
"isFinite",
|
|
83
|
+
"isNaN",
|
|
84
|
+
"isPrototypeOf",
|
|
85
|
+
"length",
|
|
86
|
+
"Math",
|
|
87
|
+
"name",
|
|
88
|
+
"NaN",
|
|
89
|
+
"Number",
|
|
90
|
+
"Object",
|
|
91
|
+
"prototype",
|
|
92
|
+
"String",
|
|
93
|
+
"toString",
|
|
94
|
+
"undefined",
|
|
95
|
+
"valueOf"
|
|
96
|
+
]);
|
|
97
|
+
/**
|
|
98
|
+
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
14
99
|
*
|
|
15
100
|
* @example
|
|
16
101
|
* ```ts
|
|
17
|
-
*
|
|
102
|
+
* isValidVarName('status') // true
|
|
103
|
+
* isValidVarName('class') // false (reserved word)
|
|
104
|
+
* isValidVarName('42foo') // false (starts with digit)
|
|
18
105
|
* ```
|
|
19
106
|
*/
|
|
20
|
-
function
|
|
21
|
-
return
|
|
22
|
-
|
|
23
|
-
extname,
|
|
24
|
-
tag: node.tags[0] ?? "default",
|
|
25
|
-
path: node.path
|
|
26
|
-
};
|
|
107
|
+
function isValidVarName(name) {
|
|
108
|
+
if (!name || reservedWords.has(name)) return false;
|
|
109
|
+
return isIdentifier(name);
|
|
27
110
|
}
|
|
28
111
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* operation. The plugin itself and every plugin that imports from it call this, so they all get
|
|
32
|
-
* the same path.
|
|
112
|
+
* Returns `name` when it's a syntactically valid JavaScript variable name,
|
|
113
|
+
* otherwise prefixes it with `_` so the result is a valid identifier.
|
|
33
114
|
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* Only the file name changes. The declarations keep their names.
|
|
115
|
+
* Useful for sanitizing OpenAPI schema names or operation IDs that start with
|
|
116
|
+
* a digit (e.g. `409`, `504AccountCancel`) before using them as exported
|
|
117
|
+
* variable, type, or function names.
|
|
38
118
|
*
|
|
39
|
-
* @example
|
|
119
|
+
* @example
|
|
40
120
|
* ```ts
|
|
41
|
-
*
|
|
121
|
+
* ensureValidVarName('409') // '_409'
|
|
122
|
+
* ensureValidVarName('504AccountCancel') // '_504AccountCancel'
|
|
123
|
+
* ensureValidVarName('Pet') // 'Pet'
|
|
124
|
+
* ensureValidVarName('class') // '_class'
|
|
42
125
|
* ```
|
|
43
126
|
*/
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const location = {
|
|
48
|
-
root,
|
|
49
|
-
output,
|
|
50
|
-
group: options.group ?? void 0
|
|
51
|
-
};
|
|
52
|
-
return memo(`${resolver.pluginName}:operationFile:${node.operationId}`, () => {
|
|
53
|
-
const schemaPaths = memo(`${resolver.pluginName}:schemaFilePaths`, () => new Set((ctx.driver.inputNode?.schemas ?? []).flatMap((schema) => schema.name ? [resolver.file({
|
|
54
|
-
name: schema.name,
|
|
55
|
-
extname: ".ts",
|
|
56
|
-
...location
|
|
57
|
-
}).path.toLowerCase()] : [])));
|
|
58
|
-
let file = resolver.file({
|
|
59
|
-
...operationFileEntry(node, node.operationId),
|
|
60
|
-
...location
|
|
61
|
-
});
|
|
62
|
-
for (let name = `${node.operationId}Operation`; schemaPaths.has(file.path.toLowerCase()); name += "Operation") {
|
|
63
|
-
const next = resolver.file({
|
|
64
|
-
...operationFileEntry(node, name),
|
|
65
|
-
...location
|
|
66
|
-
});
|
|
67
|
-
if (next.path === file.path) break;
|
|
68
|
-
file = next;
|
|
69
|
-
}
|
|
70
|
-
return file;
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
function baseContentType(contentType) {
|
|
74
|
-
return contentType.split(";")[0].trim().toLowerCase();
|
|
127
|
+
function ensureValidVarName(name) {
|
|
128
|
+
if (!name || isValidVarName(name)) return name;
|
|
129
|
+
return `_${name}`;
|
|
75
130
|
}
|
|
76
131
|
/**
|
|
77
|
-
*
|
|
78
|
-
*
|
|
132
|
+
* Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
|
|
133
|
+
*
|
|
134
|
+
* Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
|
|
135
|
+
* even though they are not valid variable names, so use this (not {@link isValidVarName}) when
|
|
136
|
+
* deciding whether an object key needs quoting.
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```ts
|
|
140
|
+
* isIdentifier('name') // true
|
|
141
|
+
* isIdentifier('x-total')// false
|
|
142
|
+
* ```
|
|
79
143
|
*/
|
|
80
|
-
function
|
|
81
|
-
|
|
82
|
-
return Boolean(entry.stream) && baseType !== "text/event-stream" && !baseType.startsWith("multipart/");
|
|
83
|
-
}
|
|
84
|
-
function isJsonContentType(baseType) {
|
|
85
|
-
return baseType === "application/json" || baseType.endsWith("+json") || baseType === "text/json";
|
|
144
|
+
function isIdentifier(name) {
|
|
145
|
+
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
86
146
|
}
|
|
147
|
+
// ../../internals/utils/src/codegen.ts
|
|
87
148
|
/**
|
|
88
|
-
*
|
|
149
|
+
* Assembles a multi-line object literal from already-rendered `entries`, indenting each entry one
|
|
150
|
+
* level and closing the brace at column zero. Entries that are themselves multi-line objects indent
|
|
151
|
+
* cumulatively. Each entry ends with a trailing comma to match the formatter's multi-line style.
|
|
89
152
|
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* buildObject(['id: z.number()', 'name: z.string()'])
|
|
156
|
+
* // '{\n id: z.number(),\n name: z.string(),\n}'
|
|
157
|
+
* ```
|
|
94
158
|
*/
|
|
95
|
-
function
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (baseType === "text/event-stream" || isJsonLines(entry)) return "stream";
|
|
99
|
-
if (isJsonContentType(baseType)) return void 0;
|
|
100
|
-
if (entry.schema?.type === "blob") return "blob";
|
|
101
|
-
if (baseType.startsWith("text/")) return "text";
|
|
102
|
-
if (isBinaryContentType(baseType)) return "blob";
|
|
103
|
-
}
|
|
104
|
-
function isBinaryContentType(baseType) {
|
|
105
|
-
return baseType === "application/octet-stream" || baseType === "application/pdf" || /^(image|audio|video)\//.test(baseType);
|
|
159
|
+
function buildObject(entries) {
|
|
160
|
+
if (entries.length === 0) return "{}";
|
|
161
|
+
return `{\n${entries.map((entry) => `${_kubb_kit.text.indent({ text: entry })},`).join("\n")}\n}`;
|
|
106
162
|
}
|
|
107
163
|
/**
|
|
108
|
-
*
|
|
109
|
-
*
|
|
164
|
+
* Assembles a bracketed list (array by default) from already-rendered `items`. Keeps everything on
|
|
165
|
+
* one line when no item spans multiple lines, and otherwise puts each item on its own line, indented
|
|
166
|
+
* one level with a trailing comma and the closing bracket at column zero. Used for member lists such
|
|
167
|
+
* as `z.union([…])` and `z.array([…])`.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```ts
|
|
171
|
+
* buildList(['z.string()', 'z.number()'])
|
|
172
|
+
* // '[z.string(), z.number()]'
|
|
173
|
+
* ```
|
|
110
174
|
*/
|
|
111
|
-
function
|
|
112
|
-
const
|
|
113
|
-
if (
|
|
114
|
-
if (
|
|
115
|
-
|
|
116
|
-
const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
117
|
-
if (parts.length === 0) return "Unknown";
|
|
118
|
-
return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
175
|
+
function buildList(items, brackets = ["[", "]"]) {
|
|
176
|
+
const [open, close] = brackets;
|
|
177
|
+
if (items.length === 0) return `${open}${close}`;
|
|
178
|
+
if (!items.some((item) => item.includes("\n"))) return `${open}${items.join(", ")}${close}`;
|
|
179
|
+
return `${open}\n${items.map((item) => `${_kubb_kit.text.indent({ text: item })},`).join("\n")}\n${close}`;
|
|
119
180
|
}
|
|
120
181
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
182
|
+
* Emits a lazy getter for a circular-ref property position, `get name() { return body }`. The key
|
|
183
|
+
* is quoted only when it is not a valid identifier. Used by the string printers to defer evaluation
|
|
184
|
+
* of a recursive schema until first access.
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```ts
|
|
188
|
+
* lazyGetter({ name: 'parent', body: 'z.lazy(() => Pet)' })
|
|
189
|
+
* // "get parent() { return z.lazy(() => Pet) }"
|
|
190
|
+
* ```
|
|
123
191
|
*/
|
|
124
|
-
function
|
|
125
|
-
|
|
126
|
-
return baseName + suffix;
|
|
192
|
+
function lazyGetter({ name, body }) {
|
|
193
|
+
return `get ${objectKey(name)}() { return ${body} }`;
|
|
127
194
|
}
|
|
128
195
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
196
|
+
* An object key, quoted only when it is not identifier-shaped. Reserved words such as `class` stay bare.
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```ts
|
|
200
|
+
* objectKey('x-total') // "'x-total'"
|
|
201
|
+
* ```
|
|
133
202
|
*/
|
|
134
|
-
function
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
203
|
+
function objectKey(name) {
|
|
204
|
+
return isIdentifier(name) ? name : _kubb_kit.text.quote(name);
|
|
205
|
+
}
|
|
206
|
+
// ../../internals/utils/src/fs.ts
|
|
207
|
+
/**
|
|
208
|
+
* Builds a nested file path from a dotted name. Splits on dots that precede a letter
|
|
209
|
+
* (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
|
|
210
|
+
* every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
|
|
211
|
+
*
|
|
212
|
+
* Empty segments are dropped before joining. They arise when the name starts with a dot
|
|
213
|
+
* followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
|
|
214
|
+
* an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
|
|
215
|
+
* absolute path, letting generated files escape the configured output directory.
|
|
216
|
+
*
|
|
217
|
+
* @example Nested path from a dotted name
|
|
218
|
+
* `toFilePath('pet.petId') // 'pet/petId'`
|
|
219
|
+
*
|
|
220
|
+
* @example PascalCase the final segment
|
|
221
|
+
* `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
|
|
222
|
+
*
|
|
223
|
+
* @example Suffix applied to the final segment only
|
|
224
|
+
* `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
|
|
225
|
+
*/
|
|
226
|
+
function toFilePath(name, caseLast = _kubb_kit.casing.camel) {
|
|
227
|
+
const parts = name.split(/\.(?=[a-zA-Z])/);
|
|
228
|
+
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : _kubb_kit.casing.camel(part)).filter(Boolean).join("/");
|
|
229
|
+
}
|
|
230
|
+
// ../../internals/utils/src/strings.ts
|
|
231
|
+
const NEEDS_UNICODE = /(?<!\\)(?:\\\\)*\\[pPu]\{|[\u{10000}-\u{10FFFF}]/u;
|
|
232
|
+
const KEPT_ESCAPE = /[A-Za-z0-9^$\\.*+?()[\]{}|/]/;
|
|
233
|
+
/**
|
|
234
|
+
* Drops the backslash from escapes that the `u` flag rejects: an escaped punctuation character that is
|
|
235
|
+
* not a syntax character, and `\-` outside a class. Both match the same character without the backslash.
|
|
236
|
+
*/
|
|
237
|
+
function dropIdentityEscapes(pattern) {
|
|
238
|
+
let inClass = false;
|
|
239
|
+
return pattern.replace(/\\(.)|[[\]]/gs, (match, escaped) => {
|
|
240
|
+
if (escaped === void 0) {
|
|
241
|
+
inClass = match === "[";
|
|
242
|
+
return match;
|
|
243
|
+
}
|
|
244
|
+
return KEPT_ESCAPE.test(escaped) || escaped === "-" && inClass ? match : escaped;
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Builds the `RegExp` for a pattern, adding the `u` flag when it uses `\p{...}`, `\P{...}` or `\u{...}`, or
|
|
249
|
+
* holds a character outside the BMP. A pattern that is not valid under `u` keeps the flags it came with.
|
|
250
|
+
*/
|
|
251
|
+
function toRegExp(pattern, flags) {
|
|
252
|
+
if (flags?.includes("u") || !NEEDS_UNICODE.test(pattern)) return new RegExp(pattern, flags);
|
|
253
|
+
try {
|
|
254
|
+
return new RegExp(dropIdentityEscapes(pattern), `${flags ?? ""}u`);
|
|
255
|
+
} catch {
|
|
256
|
+
return new RegExp(pattern, flags);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
|
|
261
|
+
* Inline flags expressed as a `^(?im)` prefix are extracted and applied to the resulting expression.
|
|
262
|
+
* Pass `null` as the second argument to emit a `/pattern/flags` literal instead.
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* ```ts
|
|
266
|
+
* toRegExpString('^(?im)foo') // 'new RegExp("^foo", "im")'
|
|
267
|
+
* toRegExpString('^(?im)foo', null) // '/^foo/im'
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
function toRegExpString(pattern, func = "RegExp") {
|
|
271
|
+
const raw = _kubb_kit.text.unquote(pattern);
|
|
272
|
+
const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
|
|
273
|
+
const replacementTarget = match?.[1] ?? "";
|
|
274
|
+
const matchedFlags = match?.[2];
|
|
275
|
+
const { source, flags } = toRegExp(raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, ""), matchedFlags);
|
|
276
|
+
if (func === null) return `/${source}/${flags}`;
|
|
277
|
+
return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
|
|
278
|
+
}
|
|
279
|
+
// ../../internals/shared/src/operation.ts
|
|
280
|
+
/**
|
|
281
|
+
* Builds the `ResolverFileParams` every operation generator passes to
|
|
282
|
+
* `resolver.file`: a file named `name`, tagged by the operation's first
|
|
283
|
+
* tag (or `'default'`), at the operation's path. Centralizes the entry object
|
|
284
|
+
* that was repeated at dozens of call sites across the client and query plugins.
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* ```ts
|
|
288
|
+
* resolver.file(operationFileEntry(node, node.operationId), { root, output, group })
|
|
289
|
+
* ```
|
|
290
|
+
*/
|
|
291
|
+
function operationFileEntry(node, name, extname = ".ts") {
|
|
292
|
+
return {
|
|
293
|
+
name,
|
|
294
|
+
extname,
|
|
295
|
+
tag: node.tags[0] ?? "default",
|
|
296
|
+
path: node.path
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Resolves the file a schema-emitting plugin (`plugin-ts`, `plugin-zod`, `plugin-faker`) writes
|
|
301
|
+
* `node` to, cached in `ctx.memo` under the resolver's plugin name and the
|
|
302
|
+
* operation. The plugin itself and every plugin that imports from it call this, so they all get
|
|
303
|
+
* the same path.
|
|
304
|
+
*
|
|
305
|
+
* When that path matches the file of a component schema in `ctx.driver.inputNode`, ignoring case, the file name
|
|
306
|
+
* gets an `Operation` suffix: operation `point` goes to `PointOperation.ts` next to schema `Point.ts`.
|
|
307
|
+
* Otherwise the two merge into one file, or overwrite each other on a case-insensitive file system.
|
|
308
|
+
* Only the file name changes. The declarations keep their names.
|
|
309
|
+
*
|
|
310
|
+
* @example Resolve `plugin-ts`'s file for the current operation
|
|
311
|
+
* ```ts
|
|
312
|
+
* const fileTs = resolveOperationFile({ ctx, node, resolver: tsResolver, root, output })
|
|
313
|
+
* ```
|
|
314
|
+
*/
|
|
315
|
+
function resolveOperationFile(options) {
|
|
316
|
+
const { ctx, node, resolver, root, output } = options;
|
|
317
|
+
const { memo } = ctx;
|
|
318
|
+
const location = {
|
|
319
|
+
root,
|
|
320
|
+
output,
|
|
321
|
+
group: options.group ?? void 0
|
|
322
|
+
};
|
|
323
|
+
return memo(`${resolver.pluginName}:operationFile:${node.operationId}`, () => {
|
|
324
|
+
const schemaPaths = memo(`${resolver.pluginName}:schemaFilePaths`, () => new Set((ctx.driver.inputNode?.schemas ?? []).flatMap((schema) => schema.name ? [resolver.file({
|
|
325
|
+
name: schema.name,
|
|
326
|
+
extname: ".ts",
|
|
327
|
+
...location
|
|
328
|
+
}).path.toLowerCase()] : [])));
|
|
329
|
+
let file = resolver.file({
|
|
330
|
+
...operationFileEntry(node, node.operationId),
|
|
331
|
+
...location
|
|
332
|
+
});
|
|
333
|
+
for (let name = `${node.operationId}Operation`; schemaPaths.has(file.path.toLowerCase()); name += "Operation") {
|
|
334
|
+
const next = resolver.file({
|
|
335
|
+
...operationFileEntry(node, name),
|
|
336
|
+
...location
|
|
337
|
+
});
|
|
338
|
+
if (next.path === file.path) break;
|
|
339
|
+
file = next;
|
|
340
|
+
}
|
|
341
|
+
return file;
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* How a content entry is framed as a stream: `'events'` for Server-Sent Events, `'lines'` for JSON Lines.
|
|
346
|
+
*/
|
|
347
|
+
function getStreamFraming(entry) {
|
|
348
|
+
if (_kubb_kit.ast.mediaType.getBase(entry) === "text/event-stream") return "events";
|
|
349
|
+
return entry.stream && _kubb_kit.ast.mediaType.isStream(entry) ? "lines" : void 0;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Parser for one response content entry.
|
|
353
|
+
*
|
|
354
|
+
* JSON stays unset so the runtime detects it. `text/event-stream` and JSON Lines are streams. A binary schema
|
|
355
|
+
* (`format: binary` / `contentMediaType`) is a blob even when the media type is not a well-known
|
|
356
|
+
* binary type. Other `text/*` is text, and `application/octet-stream`, `application/pdf`,
|
|
357
|
+
* `image/*`, `audio/*`, and `video/*` are blobs.
|
|
358
|
+
*/
|
|
359
|
+
function responseTypeForContent(entry) {
|
|
360
|
+
const baseType = _kubb_kit.ast.mediaType.getBase(entry);
|
|
361
|
+
if (!baseType) return void 0;
|
|
362
|
+
if (getStreamFraming(entry)) return "stream";
|
|
363
|
+
if (_kubb_kit.ast.mediaType.isJson(baseType)) return void 0;
|
|
364
|
+
if (entry.schema?.type === "blob") return "blob";
|
|
365
|
+
if (baseType.startsWith("text/")) return "text";
|
|
366
|
+
if (isBinaryContentType(baseType)) return "blob";
|
|
367
|
+
}
|
|
368
|
+
function isBinaryContentType(baseType) {
|
|
369
|
+
return baseType === "application/octet-stream" || baseType === "application/pdf" || /^(image|audio|video)\//.test(baseType);
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Maps a content type to the PascalCase suffix used to name per-content-type variants
|
|
373
|
+
* (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
|
|
374
|
+
*/
|
|
375
|
+
function getContentTypeSuffix(contentType) {
|
|
376
|
+
const baseType = contentType.split(";")[0].trim();
|
|
377
|
+
if (baseType === "application/json") return "Json";
|
|
378
|
+
if (baseType === "multipart/form-data") return "FormData";
|
|
379
|
+
if (baseType === "application/x-www-form-urlencoded") return "FormUrlEncoded";
|
|
380
|
+
const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
|
|
381
|
+
if (parts.length === 0) return "Unknown";
|
|
382
|
+
return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Appends a content-type suffix to a base name, keeping a trailing `Data` segment last
|
|
386
|
+
* (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).
|
|
387
|
+
*/
|
|
388
|
+
function getPerContentTypeName(baseName, suffix) {
|
|
389
|
+
if (baseName.endsWith("Data")) return suffix.endsWith("Data") ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`;
|
|
390
|
+
return baseName + suffix;
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Returns the entry with the schema of the value the client returns: `string` for a body read as text, such as `Pet` on
|
|
394
|
+
* `application/xml`, and `blob` for `image/*`, `audio/*`, `video/*`, PDF, and octet-stream. A binary schema elsewhere is a `Blob`
|
|
395
|
+
* only through `parseAs: 'blob'`, which needs every entry in `entries` binary. JSON, vendor (`application/vnd.*`), stream,
|
|
396
|
+
* schemaless, and wildcard entries keep their schema. `entries` is the response's full list of content types.
|
|
397
|
+
*/
|
|
398
|
+
function toResponseContent(entry, entries = [entry]) {
|
|
399
|
+
const { contentType, schema } = entry;
|
|
400
|
+
if (!schema || entry.stream) return entry;
|
|
401
|
+
const baseType = _kubb_kit.ast.mediaType.getBase(contentType);
|
|
402
|
+
const resolved = _kubb_kit.ast.ref.getSchema(schema);
|
|
403
|
+
if (isBinaryContentType(baseType)) return resolved.type === "blob" ? entry : {
|
|
404
|
+
...entry,
|
|
405
|
+
schema: _kubb_kit.ast.factory.createSchema({ type: "blob" }),
|
|
406
|
+
keysToOmit: null
|
|
407
|
+
};
|
|
408
|
+
const isPlainMediaType = Boolean(baseType) && !_kubb_kit.ast.mediaType.isJson(baseType) && !baseType.includes("*");
|
|
145
409
|
if (resolved.type === "blob") return isPlainMediaType && !entries.every((item) => item.schema?.type === "blob") ? {
|
|
146
410
|
...entry,
|
|
147
411
|
schema: _kubb_kit.ast.factory.createSchema({ type: "string" }),
|
|
@@ -184,50 +448,6 @@ function resolveContentTypeVariants(entries, baseName) {
|
|
|
184
448
|
};
|
|
185
449
|
});
|
|
186
450
|
}
|
|
187
|
-
const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
|
|
188
|
-
/**
|
|
189
|
-
* An OpenAPI 3.2 `querystring` parameter is the whole query string, so a form-encoded object becomes one query parameter
|
|
190
|
-
* per property, typed and serialized like any other.
|
|
191
|
-
* Another content type, such as JSON, stays one parameter named after it, and the runtime sends its value as the whole query string.
|
|
192
|
-
*/
|
|
193
|
-
function spreadQuerystring(param) {
|
|
194
|
-
const schema = param.schema.type === "ref" ? param.schema.schema : param.schema;
|
|
195
|
-
const formEncoded = !param.contentType || param.contentType.split(";")[0].trim().toLowerCase() === "application/x-www-form-urlencoded";
|
|
196
|
-
if (schema?.type !== "object" || !formEncoded) return [param];
|
|
197
|
-
return schema.properties.map((property) => _kubb_kit.ast.factory.createParameter({
|
|
198
|
-
name: property.name,
|
|
199
|
-
in: "query",
|
|
200
|
-
schema: property.schema,
|
|
201
|
-
required: param.required && property.required
|
|
202
|
-
}));
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
|
|
206
|
-
* group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
|
|
207
|
-
* (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
|
|
208
|
-
* same parameters once per plugin.
|
|
209
|
-
*/
|
|
210
|
-
function getOperationParameters(node) {
|
|
211
|
-
const cached = operationParameterGroupsByNode.get(node);
|
|
212
|
-
if (cached) return cached;
|
|
213
|
-
const dedupe = (params) => {
|
|
214
|
-
const seen = /* @__PURE__ */ new Set();
|
|
215
|
-
return params.filter((param) => {
|
|
216
|
-
if (seen.has(param.name)) return false;
|
|
217
|
-
seen.add(param.name);
|
|
218
|
-
return true;
|
|
219
|
-
});
|
|
220
|
-
};
|
|
221
|
-
const group = (location) => dedupe(node.parameters.filter((param) => param.in === location));
|
|
222
|
-
const groups = {
|
|
223
|
-
path: group("path"),
|
|
224
|
-
query: dedupe([...group("query"), ...group("querystring").flatMap(spreadQuerystring)]),
|
|
225
|
-
header: group("header"),
|
|
226
|
-
cookie: group("cookie")
|
|
227
|
-
};
|
|
228
|
-
operationParameterGroupsByNode.set(node, groups);
|
|
229
|
-
return groups;
|
|
230
|
-
}
|
|
231
451
|
/**
|
|
232
452
|
* Whether the operation declares more than one request media type with a schema, so a caller must pick one on `contentType`.
|
|
233
453
|
*/
|
|
@@ -243,8 +463,8 @@ function hasRequestContentTypeChoice(node) {
|
|
|
243
463
|
* than a record.
|
|
244
464
|
*/
|
|
245
465
|
function buildOptionsSchema(node, resolver, { pairContentTypes = false } = {}) {
|
|
246
|
-
const { path, query, header, cookie } =
|
|
247
|
-
const hasBody = Boolean(
|
|
466
|
+
const { path, query, header, cookie } = _kubb_kit.ast.http.groupParameters(node);
|
|
467
|
+
const hasBody = Boolean(_kubb_kit.ast.http.requestBody.getSchema(node));
|
|
248
468
|
const createNever = () => _kubb_kit.ast.factory.createSchema({
|
|
249
469
|
type: "never",
|
|
250
470
|
primitive: void 0,
|
|
@@ -317,438 +537,101 @@ function buildOptionsSchema(node, resolver, { pairContentTypes = false } = {}) {
|
|
|
317
537
|
required: index > 0,
|
|
318
538
|
schema: _kubb_kit.ast.factory.createSchema({
|
|
319
539
|
type: "union",
|
|
320
|
-
members: [contentType, _kubb_kit.ast.factory.createSchema({
|
|
321
|
-
type: "object",
|
|
322
|
-
primitive: "object",
|
|
323
|
-
properties: [_kubb_kit.ast.factory.createProperty({
|
|
324
|
-
name: "request",
|
|
325
|
-
required: true,
|
|
326
|
-
schema: contentType
|
|
327
|
-
})]
|
|
328
|
-
})]
|
|
329
|
-
})
|
|
330
|
-
})]);
|
|
331
|
-
})
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* The schema a status occupies in the `<Name>Responses` record. A status that documents several
|
|
336
|
-
* content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type
|
|
337
|
-
* on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the
|
|
338
|
-
* query hooks and `result.data` use.
|
|
339
|
-
*/
|
|
340
|
-
function buildResponseRecordEntry(node, res, resolver) {
|
|
341
|
-
const statusName = resolver.response.status(node, res.statusCode);
|
|
342
|
-
const variants = (res.content ?? []).filter((entry) => entry.schema);
|
|
343
|
-
if (variants.length <= 1) return _kubb_kit.ast.factory.createSchema({
|
|
344
|
-
type: "ref",
|
|
345
|
-
name: statusName
|
|
346
|
-
});
|
|
347
|
-
return _kubb_kit.ast.factory.createSchema({
|
|
348
|
-
type: "union",
|
|
349
|
-
members: resolveContentTypeVariants(variants, statusName).map((variant) => _kubb_kit.ast.factory.createSchema({
|
|
350
|
-
type: "object",
|
|
351
|
-
primitive: "object",
|
|
352
|
-
properties: [_kubb_kit.ast.factory.createProperty({
|
|
353
|
-
name: "contentType",
|
|
354
|
-
required: true,
|
|
355
|
-
schema: _kubb_kit.ast.factory.createSchema({
|
|
356
|
-
type: "enum",
|
|
357
|
-
enumValues: [baseContentType(variant.contentType)]
|
|
358
|
-
})
|
|
359
|
-
}), _kubb_kit.ast.factory.createProperty({
|
|
360
|
-
name: "data",
|
|
361
|
-
required: true,
|
|
362
|
-
schema: _kubb_kit.ast.factory.createSchema({
|
|
363
|
-
type: "ref",
|
|
364
|
-
name: variant.name
|
|
365
|
-
})
|
|
366
|
-
})]
|
|
367
|
-
}))
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
/**
|
|
371
|
-
* Builds the per-status `<Name>Responses` record for an operation, referencing the already-resolved
|
|
372
|
-
* `<Name>StatusNNN` names. Shared by `@kubb/plugin-ts`'s `Responses` type and `@kubb/plugin-zod`'s
|
|
373
|
-
* inferred responses schema, so both emit the same shape from the same inputs.
|
|
374
|
-
*
|
|
375
|
-
* Always emits the keyed record, even when an operation declares no responses. An operation with no
|
|
376
|
-
* responses renders as an empty object, which keeps every consumer's import (for example the axios
|
|
377
|
-
* SDK's `RequestResult<XResponses>`) resolvable instead of pointing at a missing export.
|
|
378
|
-
*/
|
|
379
|
-
function buildResponses(node, resolver) {
|
|
380
|
-
return _kubb_kit.ast.factory.createSchema({
|
|
381
|
-
type: "object",
|
|
382
|
-
primitive: "object",
|
|
383
|
-
properties: node.responses.map((res) => _kubb_kit.ast.factory.createProperty({
|
|
384
|
-
name: String(res.statusCode),
|
|
385
|
-
required: true,
|
|
386
|
-
schema: buildResponseRecordEntry(node, res, resolver)
|
|
387
|
-
}))
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
function getStatusCodeNumber(statusCode) {
|
|
391
|
-
const code = Number(statusCode);
|
|
392
|
-
return Number.isNaN(code) ? null : code;
|
|
393
|
-
}
|
|
394
|
-
function isSuccessStatusCode(statusCode) {
|
|
395
|
-
const code = getStatusCodeNumber(statusCode);
|
|
396
|
-
return code !== null && code >= 200 && code < 300;
|
|
397
|
-
}
|
|
398
|
-
function getSuccessResponses(responses) {
|
|
399
|
-
return responses.filter((response) => isSuccessStatusCode(response.statusCode));
|
|
400
|
-
}
|
|
401
|
-
// ../../internals/shared/src/adapter.ts
|
|
402
|
-
/**
|
|
403
|
-
* Narrows the generic `Adapter` from a generator context to the OpenAPI adapter,
|
|
404
|
-
* so OAS-only options (`dateType`, `enums`) and the parsed `document` are typed.
|
|
405
|
-
*
|
|
406
|
-
* Throws when a non-OAS adapter is configured, turning a silently wrong cast into a
|
|
407
|
-
* clear, actionable error at the point of use.
|
|
408
|
-
*
|
|
409
|
-
* @example
|
|
410
|
-
* ```ts
|
|
411
|
-
* const { dateType } = getOasAdapter(ctx.adapter).options
|
|
412
|
-
* ```
|
|
413
|
-
*/
|
|
414
|
-
function getOasAdapter(adapter) {
|
|
415
|
-
if (adapter.name !== "oas") throw new _kubb_kit.Diagnostics.Error({
|
|
416
|
-
code: _kubb_kit.Diagnostics.code.adapterRequired,
|
|
417
|
-
severity: "error",
|
|
418
|
-
message: `Expected the OpenAPI adapter (adapterOas), but received "${adapter.name}".`,
|
|
419
|
-
help: "Set `adapter: adapterOas()` in kubb.config.ts.",
|
|
420
|
-
location: { kind: "config" }
|
|
421
|
-
});
|
|
422
|
-
return adapter;
|
|
423
|
-
}
|
|
424
|
-
// ../../internals/utils/src/reserved.ts
|
|
425
|
-
/**
|
|
426
|
-
* JavaScript and Java reserved words.
|
|
427
|
-
* @link https://github.com/jonschlinkert/reserved/blob/master/index.js
|
|
428
|
-
*/
|
|
429
|
-
const reservedWords = /* @__PURE__ */ new Set([
|
|
430
|
-
"abstract",
|
|
431
|
-
"arguments",
|
|
432
|
-
"await",
|
|
433
|
-
"boolean",
|
|
434
|
-
"break",
|
|
435
|
-
"byte",
|
|
436
|
-
"case",
|
|
437
|
-
"catch",
|
|
438
|
-
"char",
|
|
439
|
-
"class",
|
|
440
|
-
"const",
|
|
441
|
-
"continue",
|
|
442
|
-
"debugger",
|
|
443
|
-
"default",
|
|
444
|
-
"delete",
|
|
445
|
-
"do",
|
|
446
|
-
"double",
|
|
447
|
-
"else",
|
|
448
|
-
"enum",
|
|
449
|
-
"eval",
|
|
450
|
-
"export",
|
|
451
|
-
"extends",
|
|
452
|
-
"false",
|
|
453
|
-
"final",
|
|
454
|
-
"finally",
|
|
455
|
-
"float",
|
|
456
|
-
"for",
|
|
457
|
-
"function",
|
|
458
|
-
"goto",
|
|
459
|
-
"if",
|
|
460
|
-
"implements",
|
|
461
|
-
"import",
|
|
462
|
-
"in",
|
|
463
|
-
"instanceof",
|
|
464
|
-
"int",
|
|
465
|
-
"interface",
|
|
466
|
-
"let",
|
|
467
|
-
"long",
|
|
468
|
-
"native",
|
|
469
|
-
"new",
|
|
470
|
-
"null",
|
|
471
|
-
"package",
|
|
472
|
-
"private",
|
|
473
|
-
"protected",
|
|
474
|
-
"public",
|
|
475
|
-
"return",
|
|
476
|
-
"short",
|
|
477
|
-
"static",
|
|
478
|
-
"super",
|
|
479
|
-
"switch",
|
|
480
|
-
"synchronized",
|
|
481
|
-
"this",
|
|
482
|
-
"throw",
|
|
483
|
-
"throws",
|
|
484
|
-
"transient",
|
|
485
|
-
"true",
|
|
486
|
-
"try",
|
|
487
|
-
"typeof",
|
|
488
|
-
"var",
|
|
489
|
-
"void",
|
|
490
|
-
"volatile",
|
|
491
|
-
"while",
|
|
492
|
-
"with",
|
|
493
|
-
"yield",
|
|
494
|
-
"Array",
|
|
495
|
-
"Date",
|
|
496
|
-
"hasOwnProperty",
|
|
497
|
-
"Infinity",
|
|
498
|
-
"isFinite",
|
|
499
|
-
"isNaN",
|
|
500
|
-
"isPrototypeOf",
|
|
501
|
-
"length",
|
|
502
|
-
"Math",
|
|
503
|
-
"name",
|
|
504
|
-
"NaN",
|
|
505
|
-
"Number",
|
|
506
|
-
"Object",
|
|
507
|
-
"prototype",
|
|
508
|
-
"String",
|
|
509
|
-
"toString",
|
|
510
|
-
"undefined",
|
|
511
|
-
"valueOf"
|
|
512
|
-
]);
|
|
513
|
-
/**
|
|
514
|
-
* Returns `true` when `name` is a syntactically valid JavaScript variable name.
|
|
515
|
-
*
|
|
516
|
-
* @example
|
|
517
|
-
* ```ts
|
|
518
|
-
* isValidVarName('status') // true
|
|
519
|
-
* isValidVarName('class') // false (reserved word)
|
|
520
|
-
* isValidVarName('42foo') // false (starts with digit)
|
|
521
|
-
* ```
|
|
522
|
-
*/
|
|
523
|
-
function isValidVarName(name) {
|
|
524
|
-
if (!name || reservedWords.has(name)) return false;
|
|
525
|
-
return isIdentifier(name);
|
|
526
|
-
}
|
|
527
|
-
/**
|
|
528
|
-
* Returns `name` when it's a syntactically valid JavaScript variable name,
|
|
529
|
-
* otherwise prefixes it with `_` so the result is a valid identifier.
|
|
530
|
-
*
|
|
531
|
-
* Useful for sanitizing OpenAPI schema names or operation IDs that start with
|
|
532
|
-
* a digit (e.g. `409`, `504AccountCancel`) before using them as exported
|
|
533
|
-
* variable, type, or function names.
|
|
534
|
-
*
|
|
535
|
-
* @example
|
|
536
|
-
* ```ts
|
|
537
|
-
* ensureValidVarName('409') // '_409'
|
|
538
|
-
* ensureValidVarName('504AccountCancel') // '_504AccountCancel'
|
|
539
|
-
* ensureValidVarName('Pet') // 'Pet'
|
|
540
|
-
* ensureValidVarName('class') // '_class'
|
|
541
|
-
* ```
|
|
542
|
-
*/
|
|
543
|
-
function ensureValidVarName(name) {
|
|
544
|
-
if (!name || isValidVarName(name)) return name;
|
|
545
|
-
return `_${name}`;
|
|
546
|
-
}
|
|
547
|
-
/**
|
|
548
|
-
* Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
|
|
549
|
-
*
|
|
550
|
-
* Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
|
|
551
|
-
* even though they are not valid variable names, so use this (not {@link isValidVarName}) when
|
|
552
|
-
* deciding whether an object key needs quoting.
|
|
553
|
-
*
|
|
554
|
-
* @example
|
|
555
|
-
* ```ts
|
|
556
|
-
* isIdentifier('name') // true
|
|
557
|
-
* isIdentifier('x-total')// false
|
|
558
|
-
* ```
|
|
559
|
-
*/
|
|
560
|
-
function isIdentifier(name) {
|
|
561
|
-
return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
|
|
562
|
-
}
|
|
563
|
-
// ../../internals/utils/src/strings.ts
|
|
564
|
-
/**
|
|
565
|
-
* Wraps a value in single quotes for emitting a single-quoted JavaScript string literal, escaping
|
|
566
|
-
* any backslash or single quote in the content.
|
|
567
|
-
*
|
|
568
|
-
* @example
|
|
569
|
-
* ```ts
|
|
570
|
-
* singleQuote('foo') // "'foo'"
|
|
571
|
-
* singleQuote("o'clock") // "'o\\'clock'"
|
|
572
|
-
* ```
|
|
573
|
-
*/
|
|
574
|
-
function singleQuote(value) {
|
|
575
|
-
if (value === void 0 || value === null) return "''";
|
|
576
|
-
return `'${String(value).replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
577
|
-
}
|
|
578
|
-
/**
|
|
579
|
-
* Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
|
|
580
|
-
* Returns the string unchanged when no balanced quote pair is found.
|
|
581
|
-
*
|
|
582
|
-
* @example
|
|
583
|
-
* ```ts
|
|
584
|
-
* trimQuotes('"hello"') // 'hello'
|
|
585
|
-
* trimQuotes('hello') // 'hello'
|
|
586
|
-
* ```
|
|
587
|
-
*/
|
|
588
|
-
function trimQuotes(text) {
|
|
589
|
-
if (text.length >= 2) {
|
|
590
|
-
const first = text[0];
|
|
591
|
-
const last = text[text.length - 1];
|
|
592
|
-
if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
|
|
593
|
-
}
|
|
594
|
-
return text;
|
|
595
|
-
}
|
|
596
|
-
/**
|
|
597
|
-
* Serializes a primitive to a single-quoted string literal, stripping any surrounding quotes first.
|
|
598
|
-
*
|
|
599
|
-
* Escaping runs through `JSON.stringify`, then the result switches to single quotes so the generated
|
|
600
|
-
* code matches the repo style without a formatter.
|
|
601
|
-
*
|
|
602
|
-
* @example
|
|
603
|
-
* ```ts
|
|
604
|
-
* stringify('hello') // "'hello'"
|
|
605
|
-
* stringify('"hello"') // "'hello'"
|
|
606
|
-
* ```
|
|
607
|
-
*/
|
|
608
|
-
function stringify(value) {
|
|
609
|
-
if (value === void 0 || value === null) return "''";
|
|
610
|
-
return `'${JSON.stringify(trimQuotes(value.toString())).slice(1, -1).replace(/\\"/g, "\"").replace(/'/g, "\\'")}'`;
|
|
611
|
-
}
|
|
612
|
-
const NEEDS_UNICODE = /(?<!\\)(?:\\\\)*\\[pPu]\{|[\u{10000}-\u{10FFFF}]/u;
|
|
613
|
-
const KEPT_ESCAPE = /[A-Za-z0-9^$\\.*+?()[\]{}|/]/;
|
|
614
|
-
/**
|
|
615
|
-
* Drops the backslash from escapes that the `u` flag rejects: an escaped punctuation character that is
|
|
616
|
-
* not a syntax character, and `\-` outside a class. Both match the same character without the backslash.
|
|
617
|
-
*/
|
|
618
|
-
function dropIdentityEscapes(pattern) {
|
|
619
|
-
let inClass = false;
|
|
620
|
-
return pattern.replace(/\\(.)|[[\]]/gs, (match, escaped) => {
|
|
621
|
-
if (escaped === void 0) {
|
|
622
|
-
inClass = match === "[";
|
|
623
|
-
return match;
|
|
624
|
-
}
|
|
625
|
-
return KEPT_ESCAPE.test(escaped) || escaped === "-" && inClass ? match : escaped;
|
|
540
|
+
members: [contentType, _kubb_kit.ast.factory.createSchema({
|
|
541
|
+
type: "object",
|
|
542
|
+
primitive: "object",
|
|
543
|
+
properties: [_kubb_kit.ast.factory.createProperty({
|
|
544
|
+
name: "request",
|
|
545
|
+
required: true,
|
|
546
|
+
schema: contentType
|
|
547
|
+
})]
|
|
548
|
+
})]
|
|
549
|
+
})
|
|
550
|
+
})]);
|
|
551
|
+
})
|
|
626
552
|
});
|
|
627
553
|
}
|
|
628
554
|
/**
|
|
629
|
-
*
|
|
630
|
-
*
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
try {
|
|
635
|
-
return new RegExp(dropIdentityEscapes(pattern), `${flags ?? ""}u`);
|
|
636
|
-
} catch {
|
|
637
|
-
return new RegExp(pattern, flags);
|
|
638
|
-
}
|
|
639
|
-
}
|
|
640
|
-
/**
|
|
641
|
-
* Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
|
|
642
|
-
* Inline flags expressed as a `^(?im)` prefix are extracted and applied to the resulting expression.
|
|
643
|
-
* Pass `null` as the second argument to emit a `/pattern/flags` literal instead.
|
|
644
|
-
*
|
|
645
|
-
* @example
|
|
646
|
-
* ```ts
|
|
647
|
-
* toRegExpString('^(?im)foo') // 'new RegExp("^foo", "im")'
|
|
648
|
-
* toRegExpString('^(?im)foo', null) // '/^foo/im'
|
|
649
|
-
* ```
|
|
650
|
-
*/
|
|
651
|
-
function toRegExpString(text, func = "RegExp") {
|
|
652
|
-
const raw = trimQuotes(text);
|
|
653
|
-
const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
|
|
654
|
-
const replacementTarget = match?.[1] ?? "";
|
|
655
|
-
const matchedFlags = match?.[2];
|
|
656
|
-
const { source, flags } = toRegExp(raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, ""), matchedFlags);
|
|
657
|
-
if (func === null) return `/${source}/${flags}`;
|
|
658
|
-
return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
|
|
659
|
-
}
|
|
660
|
-
// ../../internals/utils/src/codegen.ts
|
|
661
|
-
const INDENT = " ";
|
|
662
|
-
/**
|
|
663
|
-
* Indents every non-empty line of `text` by one indent level, leaving blank lines empty.
|
|
555
|
+
* The schema a status occupies in the `<Name>Responses` record. A status that documents several
|
|
556
|
+
* content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type
|
|
557
|
+
* on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the
|
|
558
|
+
* query hooks and `result.data` use. With `streamData`, the stream variant of a 2xx status holds that schema, since the
|
|
559
|
+
* client returns its body unread.
|
|
664
560
|
*/
|
|
665
|
-
function
|
|
666
|
-
|
|
667
|
-
|
|
561
|
+
function buildResponseRecordEntry(node, res, resolver, streamData) {
|
|
562
|
+
const statusName = resolver.response.status(node, res.statusCode);
|
|
563
|
+
const variants = (res.content ?? []).filter((entry) => entry.schema);
|
|
564
|
+
if (variants.length <= 1) return _kubb_kit.ast.factory.createSchema({
|
|
565
|
+
type: "ref",
|
|
566
|
+
name: statusName
|
|
567
|
+
});
|
|
568
|
+
return _kubb_kit.ast.factory.createSchema({
|
|
569
|
+
type: "union",
|
|
570
|
+
members: resolveContentTypeVariants(variants, statusName).map((variant) => _kubb_kit.ast.factory.createSchema({
|
|
571
|
+
type: "object",
|
|
572
|
+
primitive: "object",
|
|
573
|
+
properties: [_kubb_kit.ast.factory.createProperty({
|
|
574
|
+
name: "contentType",
|
|
575
|
+
required: true,
|
|
576
|
+
schema: _kubb_kit.ast.factory.createSchema({
|
|
577
|
+
type: "enum",
|
|
578
|
+
enumValues: [_kubb_kit.ast.mediaType.getBase(variant)]
|
|
579
|
+
})
|
|
580
|
+
}), _kubb_kit.ast.factory.createProperty({
|
|
581
|
+
name: "data",
|
|
582
|
+
required: true,
|
|
583
|
+
schema: streamData && _kubb_kit.ast.http.status.isSuccess(res) && _kubb_kit.ast.mediaType.isStream(variant) ? streamData : _kubb_kit.ast.factory.createSchema({
|
|
584
|
+
type: "ref",
|
|
585
|
+
name: variant.name
|
|
586
|
+
})
|
|
587
|
+
})]
|
|
588
|
+
}))
|
|
589
|
+
});
|
|
668
590
|
}
|
|
669
591
|
/**
|
|
670
|
-
*
|
|
671
|
-
*
|
|
592
|
+
* Builds the per-status `<Name>Responses` record for an operation, referencing the already-resolved
|
|
593
|
+
* `<Name>StatusNNN` names. Shared by `@kubb/plugin-ts`'s `Responses` type and `@kubb/plugin-zod`'s
|
|
594
|
+
* inferred responses schema, so both emit the same shape from the same inputs.
|
|
672
595
|
*
|
|
673
|
-
*
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
* objectKey('x-total') // "'x-total'"
|
|
677
|
-
* ```
|
|
678
|
-
*/
|
|
679
|
-
function objectKey(name) {
|
|
680
|
-
return isIdentifier(name) ? name : singleQuote(name);
|
|
681
|
-
}
|
|
682
|
-
/**
|
|
683
|
-
* Assembles a multi-line object literal from already-rendered `entries`, indenting each entry one
|
|
684
|
-
* level and closing the brace at column zero. Entries that are themselves multi-line objects indent
|
|
685
|
-
* cumulatively. Each entry ends with a trailing comma to match the formatter's multi-line style.
|
|
596
|
+
* Always emits the keyed record, even when an operation declares no responses. An operation with no
|
|
597
|
+
* responses renders as an empty object, which keeps every consumer's import (for example the axios
|
|
598
|
+
* SDK's `RequestResult<XResponses>`) resolvable instead of pointing at a missing export.
|
|
686
599
|
*
|
|
687
|
-
*
|
|
688
|
-
* ```ts
|
|
689
|
-
* buildObject(['id: z.number()', 'name: z.string()'])
|
|
690
|
-
* // '{\n id: z.number(),\n name: z.string(),\n}'
|
|
691
|
-
* ```
|
|
600
|
+
* `streamData` is the type of a stream variant's `data`, such as a `ReadableStream<Uint8Array>` reference.
|
|
692
601
|
*/
|
|
693
|
-
function
|
|
694
|
-
|
|
695
|
-
|
|
602
|
+
function buildResponses(node, resolver, { streamData } = {}) {
|
|
603
|
+
return _kubb_kit.ast.factory.createSchema({
|
|
604
|
+
type: "object",
|
|
605
|
+
primitive: "object",
|
|
606
|
+
properties: node.responses.map((res) => _kubb_kit.ast.factory.createProperty({
|
|
607
|
+
name: String(res.statusCode),
|
|
608
|
+
required: true,
|
|
609
|
+
schema: buildResponseRecordEntry(node, res, resolver, streamData)
|
|
610
|
+
}))
|
|
611
|
+
});
|
|
696
612
|
}
|
|
613
|
+
// ../../internals/shared/src/adapter.ts
|
|
697
614
|
/**
|
|
698
|
-
*
|
|
699
|
-
*
|
|
700
|
-
* one level with a trailing comma and the closing bracket at column zero. Used for member lists such
|
|
701
|
-
* as `z.union([…])` and `z.array([…])`.
|
|
615
|
+
* Narrows the generic `Adapter` from a generator context to the OpenAPI adapter,
|
|
616
|
+
* so OAS-only options (`dateType`, `enums`) and the parsed `document` are typed.
|
|
702
617
|
*
|
|
703
|
-
*
|
|
704
|
-
*
|
|
705
|
-
* buildList(['z.string()', 'z.number()'])
|
|
706
|
-
* // '[z.string(), z.number()]'
|
|
707
|
-
* ```
|
|
708
|
-
*/
|
|
709
|
-
function buildList(items, brackets = ["[", "]"]) {
|
|
710
|
-
const [open, close] = brackets;
|
|
711
|
-
if (items.length === 0) return `${open}${close}`;
|
|
712
|
-
if (!items.some((item) => item.includes("\n"))) return `${open}${items.join(", ")}${close}`;
|
|
713
|
-
return `${open}\n${items.map((item) => `${indentLines(item)},`).join("\n")}\n${close}`;
|
|
714
|
-
}
|
|
715
|
-
/**
|
|
716
|
-
* Emits a lazy getter for a circular-ref property position, `get name() { return body }`. The key
|
|
717
|
-
* is quoted only when it is not a valid identifier. Used by the string printers to defer evaluation
|
|
718
|
-
* of a recursive schema until first access.
|
|
618
|
+
* Throws when a non-OAS adapter is configured, turning a silently wrong cast into a
|
|
619
|
+
* clear, actionable error at the point of use.
|
|
719
620
|
*
|
|
720
621
|
* @example
|
|
721
622
|
* ```ts
|
|
722
|
-
*
|
|
723
|
-
* // "get parent() { return z.lazy(() => Pet) }"
|
|
623
|
+
* const { dateType } = getOasAdapter(ctx.adapter).options
|
|
724
624
|
* ```
|
|
725
625
|
*/
|
|
726
|
-
function
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
* Empty segments are dropped before joining. They arise when the name starts with a dot
|
|
736
|
-
* followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
|
|
737
|
-
* an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
|
|
738
|
-
* absolute path, letting generated files escape the configured output directory.
|
|
739
|
-
*
|
|
740
|
-
* @example Nested path from a dotted name
|
|
741
|
-
* `toFilePath('pet.petId') // 'pet/petId'`
|
|
742
|
-
*
|
|
743
|
-
* @example PascalCase the final segment
|
|
744
|
-
* `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
|
|
745
|
-
*
|
|
746
|
-
* @example Suffix applied to the final segment only
|
|
747
|
-
* `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
|
|
748
|
-
*/
|
|
749
|
-
function toFilePath(name, caseLast = _kubb_kit.camelCase) {
|
|
750
|
-
const parts = name.split(/\.(?=[a-zA-Z])/);
|
|
751
|
-
return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : (0, _kubb_kit.camelCase)(part)).filter(Boolean).join("/");
|
|
626
|
+
function getOasAdapter(adapter) {
|
|
627
|
+
if (adapter.name !== "oas") throw new _kubb_kit.Diagnostics.Error({
|
|
628
|
+
code: _kubb_kit.Diagnostics.code.adapterRequired,
|
|
629
|
+
severity: "error",
|
|
630
|
+
message: `Expected the OpenAPI adapter (adapterOas), but received "${adapter.name}".`,
|
|
631
|
+
help: "Set `adapter: adapterOas()` in kubb.config.ts.",
|
|
632
|
+
location: { kind: "config" }
|
|
633
|
+
});
|
|
634
|
+
return adapter;
|
|
752
635
|
}
|
|
753
636
|
// ../../internals/shared/src/resolver.ts
|
|
754
637
|
/**
|
|
@@ -838,6 +721,13 @@ function createOperationResponseResolver() {
|
|
|
838
721
|
name
|
|
839
722
|
});
|
|
840
723
|
},
|
|
724
|
+
headers(node, statusCode) {
|
|
725
|
+
const name = this.name(`${node.operationId} Status ${statusCode} Headers`);
|
|
726
|
+
return this.resolveName({
|
|
727
|
+
key: operationKey(node, "response", "headers", statusCode),
|
|
728
|
+
name
|
|
729
|
+
});
|
|
730
|
+
},
|
|
841
731
|
body(node) {
|
|
842
732
|
const name = this.name(`${node.operationId} Body`);
|
|
843
733
|
return this.resolveName({
|
|
@@ -879,75 +769,7 @@ function createCasedFile(caseLast) {
|
|
|
879
769
|
* Names of the schemas in a circular reference chain, computed once per build.
|
|
880
770
|
*/
|
|
881
771
|
function getCircularNames(ctx) {
|
|
882
|
-
return ctx.memo("circularNames:set", () => _kubb_kit.ast.
|
|
883
|
-
}
|
|
884
|
-
// ../../internals/shared/src/group.ts
|
|
885
|
-
/**
|
|
886
|
-
* Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
|
|
887
|
-
* shared default naming so every plugin groups output consistently:
|
|
888
|
-
*
|
|
889
|
-
* - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
|
|
890
|
-
* - other groups use the camelCased group (`pet store` → `petStore`).
|
|
891
|
-
*
|
|
892
|
-
* A user-provided `group.name` always wins over the default namer, so callers stay in
|
|
893
|
-
* control of their output folders. Returns `null` when grouping is disabled, matching the
|
|
894
|
-
* per-plugin convention.
|
|
895
|
-
*
|
|
896
|
-
* @param group - The user-supplied group option, or `undefined` to disable grouping.
|
|
897
|
-
*
|
|
898
|
-
* @example
|
|
899
|
-
* ```ts
|
|
900
|
-
* createGroupConfig(group) // shared across every plugin
|
|
901
|
-
* ```
|
|
902
|
-
*/
|
|
903
|
-
function createGroupConfig(group) {
|
|
904
|
-
if (!group) return null;
|
|
905
|
-
const defaultName = (ctx) => {
|
|
906
|
-
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
907
|
-
return (0, _kubb_kit.camelCase)(ctx.group);
|
|
908
|
-
};
|
|
909
|
-
return {
|
|
910
|
-
...group,
|
|
911
|
-
name: group.name ? group.name : defaultName
|
|
912
|
-
};
|
|
913
|
-
}
|
|
914
|
-
// ../../internals/shared/src/schemaTraversal.ts
|
|
915
|
-
/**
|
|
916
|
-
* Maps each property of an object schema to its transformed output. Pairs every result with the
|
|
917
|
-
* original property so the printer keeps full control over modifiers, getters, and key syntax.
|
|
918
|
-
*
|
|
919
|
-
* @example
|
|
920
|
-
* ```ts
|
|
921
|
-
* const entries = mapSchemaProperties(node, (schema) => this.transform(schema))
|
|
922
|
-
* // entries: [{ name: 'id', property, output: 'z.number()' }, ...]
|
|
923
|
-
* ```
|
|
924
|
-
*/
|
|
925
|
-
function mapSchemaProperties(node, transform) {
|
|
926
|
-
return node.properties.map((property) => ({
|
|
927
|
-
name: property.name,
|
|
928
|
-
property,
|
|
929
|
-
output: transform(property.schema)
|
|
930
|
-
}));
|
|
931
|
-
}
|
|
932
|
-
/**
|
|
933
|
-
* Maps each member of a union or intersection schema to its transformed output, pairing every
|
|
934
|
-
* result with the original member.
|
|
935
|
-
*/
|
|
936
|
-
function mapSchemaMembers(node, transform) {
|
|
937
|
-
return (node.members ?? []).map((schema) => ({
|
|
938
|
-
schema,
|
|
939
|
-
output: transform(schema)
|
|
940
|
-
}));
|
|
941
|
-
}
|
|
942
|
-
/**
|
|
943
|
-
* Maps each item of an array or tuple schema to its transformed output, pairing every result with
|
|
944
|
-
* the original item.
|
|
945
|
-
*/
|
|
946
|
-
function mapSchemaItems(node, transform) {
|
|
947
|
-
return (node.items ?? []).map((schema) => ({
|
|
948
|
-
schema,
|
|
949
|
-
output: transform(schema)
|
|
950
|
-
}));
|
|
772
|
+
return ctx.memo("circularNames:set", () => _kubb_kit.ast.graph.getCircularNames(ctx.driver.inputNode?.schemas ?? []));
|
|
951
773
|
}
|
|
952
774
|
// src/components/Zod.tsx
|
|
953
775
|
function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuards, isName, assertName, mini, cyclic, compile }) {
|
|
@@ -955,9 +777,9 @@ function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuar
|
|
|
955
777
|
const printerImports = printer.drainImports();
|
|
956
778
|
if (!output) return;
|
|
957
779
|
const needsAnnotation = cyclic && node.type !== "object";
|
|
958
|
-
const
|
|
959
|
-
const isCyclic = Boolean(cyclic || printer.options.cyclicSchemas &&
|
|
960
|
-
const value = Boolean(compile) && !
|
|
780
|
+
const isAlias = _kubb_kit.ast.ref.isAlias(node, printer.options.keysToOmit);
|
|
781
|
+
const isCyclic = Boolean(cyclic || printer.options.cyclicSchemas && _kubb_kit.ast.graph.containsCircular(node, { circularSchemas: printer.options.cyclicSchemas }));
|
|
782
|
+
const value = Boolean(compile) && !isAlias && !isCyclic ? typeof compile === "object" && compile.strict ? `z.compile(${output}, { strict: true })` : `z.compile(${output})` : output;
|
|
961
783
|
const targetType = inferTypeName ?? `z.infer<typeof ${name}>`;
|
|
962
784
|
const shouldGenerateIs = isName && (typeof typeGuards === "object" ? typeGuards.is ?? true : Boolean(typeGuards));
|
|
963
785
|
const shouldGenerateAssert = assertName && (typeof typeGuards === "object" ? typeGuards.assert ?? true : Boolean(typeGuards));
|
|
@@ -980,7 +802,7 @@ function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuar
|
|
|
980
802
|
children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
|
|
981
803
|
export: true,
|
|
982
804
|
name,
|
|
983
|
-
type: needsAnnotation ? "z.ZodType" : void 0,
|
|
805
|
+
type: needsAnnotation ? mini ? "z.ZodMiniType" : "z.ZodType" : void 0,
|
|
984
806
|
children: value
|
|
985
807
|
})
|
|
986
808
|
}),
|
|
@@ -1040,7 +862,7 @@ function shouldCoerce(coercion, type) {
|
|
|
1040
862
|
*/
|
|
1041
863
|
function isClosedKeySchema(node) {
|
|
1042
864
|
if (!node) return false;
|
|
1043
|
-
const resolved =
|
|
865
|
+
const resolved = _kubb_kit.ast.ref.mergeSchema(node);
|
|
1044
866
|
if (resolved.type === "enum") return true;
|
|
1045
867
|
if (resolved.type === "union") {
|
|
1046
868
|
const members = resolved.members ?? [];
|
|
@@ -1050,12 +872,12 @@ function isClosedKeySchema(node) {
|
|
|
1050
872
|
}
|
|
1051
873
|
/**
|
|
1052
874
|
* Whether the node is a plain inline object whose shape can be lifted into an `.extend({ … })`
|
|
1053
|
-
* argument.
|
|
875
|
+
* argument. Input checks, a catchall, `patternProperties`, or a nullable/optional wrapper cannot, so those stay
|
|
1054
876
|
* on `.and(…)`.
|
|
1055
877
|
*/
|
|
1056
878
|
function isPlainInlineObject(node) {
|
|
1057
879
|
if (node.type !== "object") return false;
|
|
1058
|
-
if (node
|
|
880
|
+
if (hasInputChecks(node)) return false;
|
|
1059
881
|
if (node.nullable || node.optional || node.nullish) return false;
|
|
1060
882
|
const hasAdditionalProperties = node.additionalProperties !== void 0;
|
|
1061
883
|
const hasPatternProperties = Boolean(node.patternProperties);
|
|
@@ -1066,21 +888,21 @@ function isPlainInlineObject(node) {
|
|
|
1066
888
|
* `z.discriminatedUnion` option. Covers object nodes, `$ref`s that resolve to (or are still
|
|
1067
889
|
* unresolved) objects, single-member unions of an object, and object-composable `allOf`.
|
|
1068
890
|
*
|
|
1069
|
-
*
|
|
891
|
+
* Input checks, such as property counts and `conditionals`, render as a pipe. A ref into `cyclicSchemas` renders as `z.lazy(…)`.
|
|
1070
892
|
*/
|
|
1071
893
|
function isObjectSchemaNode(node, cyclicSchemas) {
|
|
1072
894
|
if (node.nullable || node.optional || node.nullish) return false;
|
|
895
|
+
if (hasInputChecks(node)) return false;
|
|
1073
896
|
if (node.type === "object") {
|
|
1074
|
-
if (node.minProperties !== void 0 || node.maxProperties !== void 0) return false;
|
|
1075
897
|
const hasProperties = Boolean(node.properties?.length);
|
|
1076
898
|
const hasTypedAdditionalProperties = Boolean(node.additionalProperties && node.additionalProperties !== true);
|
|
1077
899
|
const hasPatternProperties = Boolean(node.patternProperties);
|
|
1078
900
|
return !(!hasProperties && (hasTypedAdditionalProperties || hasPatternProperties || node.propertyNames));
|
|
1079
901
|
}
|
|
1080
902
|
if (node.type === "ref") {
|
|
1081
|
-
const refName = _kubb_kit.ast.
|
|
903
|
+
const refName = _kubb_kit.ast.ref.getName(node);
|
|
1082
904
|
if (refName && cyclicSchemas?.has(refName)) return false;
|
|
1083
|
-
const resolved =
|
|
905
|
+
const resolved = _kubb_kit.ast.ref.mergeSchema(node);
|
|
1084
906
|
return resolved.type === "ref" || isObjectSchemaNode(resolved, cyclicSchemas);
|
|
1085
907
|
}
|
|
1086
908
|
if (node.type === "union") {
|
|
@@ -1103,23 +925,23 @@ function preserveRequiredProperties(members) {
|
|
|
1103
925
|
const hasRequiredProperty = ({ node, propertyName, seen = /* @__PURE__ */ new Set() }) => {
|
|
1104
926
|
if (seen.has(node)) return false;
|
|
1105
927
|
seen.add(node);
|
|
1106
|
-
const resolved =
|
|
1107
|
-
const composition = _kubb_kit.ast.
|
|
928
|
+
const resolved = _kubb_kit.ast.ref.mergeSchema(node);
|
|
929
|
+
const composition = _kubb_kit.ast.schema.narrow(resolved, _kubb_kit.ast.schema.types.intersection) ?? _kubb_kit.ast.schema.narrow(resolved, _kubb_kit.ast.schema.types.union);
|
|
1108
930
|
if (composition) {
|
|
1109
|
-
if (composition.type === _kubb_kit.ast.
|
|
931
|
+
if (composition.type === _kubb_kit.ast.schema.types.union && composition.members?.length !== 1) return false;
|
|
1110
932
|
return composition.members?.some((child) => hasRequiredProperty({
|
|
1111
933
|
node: child,
|
|
1112
934
|
propertyName,
|
|
1113
935
|
seen
|
|
1114
936
|
})) ?? false;
|
|
1115
937
|
}
|
|
1116
|
-
return _kubb_kit.ast.
|
|
938
|
+
return _kubb_kit.ast.schema.findProperties({
|
|
1117
939
|
node: resolved,
|
|
1118
940
|
propertyName
|
|
1119
941
|
}).some((property) => property.required !== false && !property.schema.optional && !property.schema.nullish);
|
|
1120
942
|
};
|
|
1121
943
|
return members.map((member, index) => {
|
|
1122
|
-
const object = _kubb_kit.ast.
|
|
944
|
+
const object = _kubb_kit.ast.schema.narrow(member, _kubb_kit.ast.schema.types.object);
|
|
1123
945
|
if (!object || index === 0) return member;
|
|
1124
946
|
return {
|
|
1125
947
|
...object,
|
|
@@ -1148,7 +970,7 @@ function preserveRequiredProperties(members) {
|
|
|
1148
970
|
* representation, strings are quoted.
|
|
1149
971
|
*/
|
|
1150
972
|
function formatDefault(value) {
|
|
1151
|
-
if (typeof value === "string") return
|
|
973
|
+
if (typeof value === "string") return _kubb_kit.text.quote(value);
|
|
1152
974
|
if (typeof value === "object" && value !== null) return JSON.stringify(value);
|
|
1153
975
|
return String(value ?? "");
|
|
1154
976
|
}
|
|
@@ -1163,16 +985,17 @@ function formatDefault(value) {
|
|
|
1163
985
|
*/
|
|
1164
986
|
function defaultLiteral(node, value) {
|
|
1165
987
|
if (value === null) return null;
|
|
1166
|
-
const resolved = node ?
|
|
1167
|
-
if (resolved
|
|
988
|
+
const resolved = node ? _kubb_kit.ast.ref.mergeSchema(node) : void 0;
|
|
989
|
+
if (resolved?.type === "blob") return null;
|
|
990
|
+
if (resolved && _kubb_kit.ast.schema.narrow(resolved, "bigint")) {
|
|
1168
991
|
if (typeof value === "bigint") return `BigInt(${value})`;
|
|
1169
992
|
if (typeof value === "number" && Number.isInteger(value)) return `BigInt(${value})`;
|
|
1170
993
|
return null;
|
|
1171
994
|
}
|
|
1172
|
-
if (resolved && _kubb_kit.ast.
|
|
1173
|
-
const enumNode = resolved ? _kubb_kit.ast.
|
|
995
|
+
if (resolved && _kubb_kit.ast.schema.narrow(resolved, "array")) return Array.isArray(value) ? JSON.stringify(value) : null;
|
|
996
|
+
const enumNode = resolved ? _kubb_kit.ast.schema.narrow(resolved, "enum") : void 0;
|
|
1174
997
|
if (enumNode) {
|
|
1175
|
-
const values =
|
|
998
|
+
const values = _kubb_kit.ast.schema.getLiterals(enumNode);
|
|
1176
999
|
if (values.length) {
|
|
1177
1000
|
const match = values.find((member) => member === value || String(member) === String(value));
|
|
1178
1001
|
return match != null ? formatDefault(match) : null;
|
|
@@ -1241,13 +1064,13 @@ function patternKeySchemaMini({ patterns, regexType }) {
|
|
|
1241
1064
|
* Build `.min()` / `.max()` / `.gt()` / `.lt()` constraint chains for numbers
|
|
1242
1065
|
* using the standard chainable Zod v4 API.
|
|
1243
1066
|
*/
|
|
1244
|
-
function numberConstraints({ min, max,
|
|
1067
|
+
function numberConstraints({ min, max, exclusive, multipleOf }, suffix = "") {
|
|
1245
1068
|
return [
|
|
1246
|
-
min !== void 0 ? `.min(${min})` : "",
|
|
1247
|
-
max !== void 0 ? `.max(${max})` : "",
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
multipleOf !== void 0 ? `.multipleOf(${multipleOf})` : ""
|
|
1069
|
+
min !== void 0 ? `.min(${min}${suffix})` : "",
|
|
1070
|
+
max !== void 0 ? `.max(${max}${suffix})` : "",
|
|
1071
|
+
exclusive?.min !== void 0 ? `.gt(${exclusive.min}${suffix})` : "",
|
|
1072
|
+
exclusive?.max !== void 0 ? `.lt(${exclusive.max}${suffix})` : "",
|
|
1073
|
+
multipleOf !== void 0 ? `.multipleOf(${multipleOf}${suffix})` : ""
|
|
1251
1074
|
].join("");
|
|
1252
1075
|
}
|
|
1253
1076
|
/**
|
|
@@ -1262,36 +1085,71 @@ function lengthConstraints({ min, max, pattern, regexType }) {
|
|
|
1262
1085
|
].join("");
|
|
1263
1086
|
}
|
|
1264
1087
|
/**
|
|
1088
|
+
* Numeric bounds for a bigint: the inclusive, whole, safe bounds faker mocks against, and a whole, safe `multipleOf`.
|
|
1089
|
+
*/
|
|
1090
|
+
function bigintConstraints({ min, max, exclusive, multipleOf }) {
|
|
1091
|
+
return {
|
|
1092
|
+
..._kubb_kit.ast.schema.getNumberBounds({
|
|
1093
|
+
min,
|
|
1094
|
+
max,
|
|
1095
|
+
exclusive,
|
|
1096
|
+
integer: true
|
|
1097
|
+
}),
|
|
1098
|
+
multipleOf: Number.isSafeInteger(multipleOf) ? multipleOf : void 0
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
/**
|
|
1265
1102
|
* Build `.check(z.minimum(), z.maximum())` for `zod/mini` numeric constraints.
|
|
1266
1103
|
*/
|
|
1267
|
-
function numberChecksMini({ min, max,
|
|
1268
|
-
const checks = [
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1104
|
+
function numberChecksMini({ min, max, exclusive, multipleOf }, suffix = "") {
|
|
1105
|
+
const checks = [
|
|
1106
|
+
min !== void 0 && `z.minimum(${min}${suffix})`,
|
|
1107
|
+
max !== void 0 && `z.maximum(${max}${suffix})`,
|
|
1108
|
+
exclusive?.min !== void 0 && `z.gt(${exclusive.min}${suffix})`,
|
|
1109
|
+
exclusive?.max !== void 0 && `z.lt(${exclusive.max}${suffix})`,
|
|
1110
|
+
multipleOf !== void 0 && `z.multipleOf(${multipleOf}${suffix})`
|
|
1111
|
+
].filter(Boolean);
|
|
1274
1112
|
return checks.length ? `.check(${checks.join(", ")})` : "";
|
|
1275
1113
|
}
|
|
1276
1114
|
/**
|
|
1277
1115
|
* Build `.check(z.minLength(), z.maxLength(), z.regex())` for `zod/mini` length constraints.
|
|
1278
1116
|
*/
|
|
1279
1117
|
function lengthChecksMini({ min, max, pattern, regexType }) {
|
|
1280
|
-
const checks = [
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1118
|
+
const checks = [
|
|
1119
|
+
min !== void 0 && `z.minLength(${min})`,
|
|
1120
|
+
max !== void 0 && `z.maxLength(${max})`,
|
|
1121
|
+
pattern !== void 0 && `z.regex(${regexSource(pattern, regexType)})`
|
|
1122
|
+
].filter(Boolean);
|
|
1284
1123
|
return checks.length ? `.check(${checks.join(", ")})` : "";
|
|
1285
1124
|
}
|
|
1286
1125
|
/**
|
|
1287
|
-
*
|
|
1126
|
+
* Whether `propertyNames` needs its own check: `z.record` checks the keys of a map, but `z.object` and a record keyed by
|
|
1127
|
+
* `patternProperties` do not.
|
|
1128
|
+
*/
|
|
1129
|
+
function hasKeyCheck(node) {
|
|
1130
|
+
if (node.type !== "object" || !node.propertyNames) return false;
|
|
1131
|
+
const hasTypedAdditionalProperties = Boolean(node.additionalProperties && node.additionalProperties !== true);
|
|
1132
|
+
return node.properties.length > 0 || Boolean(node.patternProperties) && !hasTypedAdditionalProperties;
|
|
1133
|
+
}
|
|
1134
|
+
/**
|
|
1135
|
+
* Whether the node prints inside `z.pipe(z.unknown().check(…), …)`, see {@link inputChecks}.
|
|
1288
1136
|
*/
|
|
1289
|
-
function
|
|
1290
|
-
|
|
1291
|
-
|
|
1137
|
+
function hasInputChecks(node) {
|
|
1138
|
+
return Boolean(node.conditionals?.length) || node.type === "object" && (node.min !== void 0 || node.max !== void 0) || hasKeyCheck(node);
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Checks the raw input, before parsing strips unknown keys or inserts defaults: property counts, key names, then the
|
|
1142
|
+
* printed `conditionals`.
|
|
1143
|
+
*/
|
|
1144
|
+
function inputChecks({ value, node, keySchema, conditionals }) {
|
|
1292
1145
|
const skip = "typeof value !== \"object\" || value === null || Array.isArray(value)";
|
|
1293
|
-
|
|
1294
|
-
|
|
1146
|
+
const { min, max } = node.type === "object" ? node : {};
|
|
1147
|
+
const checks = [
|
|
1148
|
+
min !== void 0 && `z.refine(value => ${skip} || Object.keys(value).length >= ${min}, { message: "Object must have at least ${min} properties" })`,
|
|
1149
|
+
max !== void 0 && `z.refine(value => ${skip} || Object.keys(value).length <= ${max}, { message: "Object must have at most ${max} properties" })`,
|
|
1150
|
+
keySchema && `z.refine(value => ${skip} || Object.keys(value).every((key) => ${keySchema}.safeParse(key).success), { message: "Object keys must match the property names schema" })`,
|
|
1151
|
+
...conditionals
|
|
1152
|
+
].filter(Boolean);
|
|
1295
1153
|
return checks.length ? `z.pipe(z.unknown().check(${checks.join(", ")}), ${value})` : value;
|
|
1296
1154
|
}
|
|
1297
1155
|
/**
|
|
@@ -1307,8 +1165,8 @@ function applyModifiers({ value, schema, nullable, optional, nullish, defaultVal
|
|
|
1307
1165
|
})();
|
|
1308
1166
|
const literal = defaultValue !== void 0 ? defaultLiteral(schema, defaultValue) : null;
|
|
1309
1167
|
const withDefault = literal !== null ? `${withModifier}.default(${literal})` : withModifier;
|
|
1310
|
-
const withDescription = description ? `${withDefault}.describe(${
|
|
1311
|
-
return examples?.length ? `${withDescription}.meta({ examples: [${examples.map(formatDefault).join(", ")}] })` : withDescription;
|
|
1168
|
+
const withDescription = description ? `${withDefault}.describe(${_kubb_kit.text.quote(description)})` : withDefault;
|
|
1169
|
+
return examples?.length ? `${withDescription}.meta({ examples: [${examples.map((example) => example === null ? "null" : formatDefault(example)).join(", ")}] })` : withDescription;
|
|
1312
1170
|
}
|
|
1313
1171
|
function modifierDepth(schema) {
|
|
1314
1172
|
if (schema.nullish || schema.nullable && schema.optional) return 2;
|
|
@@ -1326,7 +1184,7 @@ function modifierDepth(schema) {
|
|
|
1326
1184
|
* `.unwrap()`, so its printer passes `._zod.def.innerType` as the `step`.
|
|
1327
1185
|
*/
|
|
1328
1186
|
function omitUnwrapChain(node, step = ".unwrap()") {
|
|
1329
|
-
const ref = _kubb_kit.ast.
|
|
1187
|
+
const ref = _kubb_kit.ast.schema.narrow(node, "ref");
|
|
1330
1188
|
if (!ref) return "";
|
|
1331
1189
|
const target = ref.schema ?? ref;
|
|
1332
1190
|
const depth = modifierDepth(target) + (target.default !== void 0 ? 1 : 0);
|
|
@@ -1348,7 +1206,7 @@ function applyMiniModifiers({ value, schema, nullable, optional, nullish, defaul
|
|
|
1348
1206
|
/**
|
|
1349
1207
|
* Calls for string `format`s with a dedicated Zod constructor; a `Map` so `constructor` cannot hit `Object.prototype`.
|
|
1350
1208
|
*/
|
|
1351
|
-
const
|
|
1209
|
+
const stringFormatCalls = /* @__PURE__ */ new Map([
|
|
1352
1210
|
["byte", "z.base64()"],
|
|
1353
1211
|
["base64", "z.base64()"],
|
|
1354
1212
|
["base64url", "z.base64url()"],
|
|
@@ -1359,6 +1217,16 @@ const STRING_FORMAT_CALLS = /* @__PURE__ */ new Map([
|
|
|
1359
1217
|
["hostname", "z.hostname()"]
|
|
1360
1218
|
]);
|
|
1361
1219
|
/**
|
|
1220
|
+
* Calls for a string's `contentEncoding` with a dedicated Zod constructor.
|
|
1221
|
+
*/
|
|
1222
|
+
const contentEncodingCalls = /* @__PURE__ */ new Map([["base64", "z.base64()"], ["base64url", "z.base64url()"]]);
|
|
1223
|
+
/**
|
|
1224
|
+
* The check for a string that holds a JSON document: it must parse, and the parsed value must pass `schema`.
|
|
1225
|
+
*/
|
|
1226
|
+
function jsonContentCheck(schema) {
|
|
1227
|
+
return `(value) => { try { return ${schema}.safeParse(JSON.parse(value)).success } catch { return false } }, { message: "String must hold JSON that matches its content schema" }`;
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1362
1230
|
* Marks an inline `oneOf` object strict, so `object()` builds `z.strictObject(...)` and no eager `.shape` read hits a deferred getter.
|
|
1363
1231
|
*/
|
|
1364
1232
|
function strictOneOfNode(node) {
|
|
@@ -1371,13 +1239,14 @@ function strictOneOfNode(node) {
|
|
|
1371
1239
|
* The constraint chain an `allOf` member adds to a bare string, number or array; each printer passes its own renderers.
|
|
1372
1240
|
*/
|
|
1373
1241
|
function getMemberConstraint({ member, regexType, lengths, numbers }) {
|
|
1242
|
+
if (hasInputChecks(member) || _kubb_kit.ast.schema.narrow(member, "array")?.contains) return void 0;
|
|
1374
1243
|
if (member.primitive === "string") return lengths({
|
|
1375
|
-
..._kubb_kit.ast.
|
|
1244
|
+
..._kubb_kit.ast.schema.narrow(member, "string") ?? {},
|
|
1376
1245
|
regexType
|
|
1377
1246
|
}) || void 0;
|
|
1378
|
-
if (member.primitive === "number" || member.primitive === "integer") return numbers(_kubb_kit.ast.
|
|
1247
|
+
if (member.primitive === "number" || member.primitive === "integer") return numbers(_kubb_kit.ast.schema.narrow(member, "number") ?? _kubb_kit.ast.schema.narrow(member, "integer") ?? {}) || void 0;
|
|
1379
1248
|
if (member.primitive === "array") return lengths({
|
|
1380
|
-
..._kubb_kit.ast.
|
|
1249
|
+
..._kubb_kit.ast.schema.narrow(member, "array") ?? {},
|
|
1381
1250
|
regexType
|
|
1382
1251
|
}) || void 0;
|
|
1383
1252
|
}
|
|
@@ -1385,24 +1254,26 @@ function getMemberConstraint({ member, regexType, lengths, numbers }) {
|
|
|
1385
1254
|
* Builds the `{ key: value, … }` shape shared by the `z.object(...)` and `.extend(...)` renderings.
|
|
1386
1255
|
*/
|
|
1387
1256
|
function buildObjectShape(ctx, node, renderProperty) {
|
|
1388
|
-
const objectNode = _kubb_kit.ast.
|
|
1257
|
+
const objectNode = _kubb_kit.ast.schema.narrow(node, "object");
|
|
1389
1258
|
if (!objectNode) return "{}";
|
|
1390
|
-
const isCyclic = (schema) => ctx.options.cyclicSchemas != null &&
|
|
1391
|
-
|
|
1259
|
+
const isCyclic = (schema) => ctx.options.cyclicSchemas != null && _kubb_kit.ast.graph.containsCircular(schema, { circularSchemas: ctx.options.cyclicSchemas });
|
|
1260
|
+
const transformed = objectNode.properties.map(({ schema }) => {
|
|
1392
1261
|
const hasSelfRef = isCyclic(schema);
|
|
1393
1262
|
const savedCyclicSchemas = ctx.options.cyclicSchemas;
|
|
1394
1263
|
if (hasSelfRef) ctx.options.cyclicSchemas = void 0;
|
|
1395
1264
|
const baseOutput = ctx.transform(schema) ?? ctx.transform(_kubb_kit.ast.factory.createSchema({ type: "unknown" }));
|
|
1396
1265
|
if (hasSelfRef) ctx.options.cyclicSchemas = savedCyclicSchemas;
|
|
1397
1266
|
return baseOutput;
|
|
1398
|
-
})
|
|
1399
|
-
|
|
1267
|
+
});
|
|
1268
|
+
return buildObject(objectNode.properties.map((property, index) => {
|
|
1269
|
+
const { name, schema } = property;
|
|
1270
|
+
const value = transformed[index];
|
|
1400
1271
|
return renderProperty({
|
|
1401
1272
|
name,
|
|
1402
1273
|
value,
|
|
1403
1274
|
schema,
|
|
1404
1275
|
required: property.required,
|
|
1405
|
-
meta:
|
|
1276
|
+
meta: _kubb_kit.ast.ref.mergeSchema(schema),
|
|
1406
1277
|
cyclic: isCyclic(schema)
|
|
1407
1278
|
});
|
|
1408
1279
|
}));
|
|
@@ -1414,14 +1285,14 @@ function printWithOmit(ctx, node, modify, mini = false) {
|
|
|
1414
1285
|
const { keysToOmit } = ctx.options;
|
|
1415
1286
|
const transformed = ctx.transform(node);
|
|
1416
1287
|
if (!transformed) return null;
|
|
1417
|
-
const meta =
|
|
1288
|
+
const meta = _kubb_kit.ast.ref.mergeSchema(node);
|
|
1418
1289
|
return modify((() => {
|
|
1419
1290
|
if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
|
|
1420
1291
|
const unwrap = omitUnwrapChain(node, mini ? "._zod.def.innerType" : ".unwrap()");
|
|
1421
1292
|
const mask = `{ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }`;
|
|
1422
1293
|
const omit = (target) => {
|
|
1423
1294
|
const inner = `${target}${unwrap}`;
|
|
1424
|
-
const hasObjectConstraints =
|
|
1295
|
+
const hasObjectConstraints = hasInputChecks(meta);
|
|
1425
1296
|
const pipe = mini ? `${inner}._zod.def` : inner;
|
|
1426
1297
|
const object = hasObjectConstraints ? `${pipe}.out` : inner;
|
|
1427
1298
|
const omitted = mini ? `z.omit(${object}, ${mask})` : `${object}.omit(${mask})`;
|
|
@@ -1440,7 +1311,7 @@ function printWithOmit(ctx, node, modify, mini = false) {
|
|
|
1440
1311
|
function getLiteralUnion({ node, options }) {
|
|
1441
1312
|
if (node.strategy === "one" && options.oneOfType === "xor" || options.nodes?.enum) return void 0;
|
|
1442
1313
|
const values = (node.members ?? []).map((member) => {
|
|
1443
|
-
const enumNode = _kubb_kit.ast.
|
|
1314
|
+
const enumNode = _kubb_kit.ast.schema.narrow(member, _kubb_kit.ast.schema.types.enum);
|
|
1444
1315
|
return enumNode && !enumNode.name && !enumNode.nullable && !enumNode.namedEnumValues && enumNode.enumValues?.length === 1 ? enumNode.enumValues[0] : void 0;
|
|
1445
1316
|
});
|
|
1446
1317
|
if (!values.length || !values.every((value) => value !== void 0 && value !== null)) return void 0;
|
|
@@ -1453,19 +1324,26 @@ function getLiteralUnion({ node, options }) {
|
|
|
1453
1324
|
* Whether a discriminated union member's discriminator property prints as a coerced literal union.
|
|
1454
1325
|
*/
|
|
1455
1326
|
function hasCoercedDiscriminator({ member, propertyName, options }) {
|
|
1456
|
-
return _kubb_kit.ast.
|
|
1327
|
+
return _kubb_kit.ast.schema.findProperties({
|
|
1457
1328
|
node: member,
|
|
1458
1329
|
propertyName
|
|
1459
1330
|
}).some(({ schema }) => {
|
|
1460
|
-
const resolved =
|
|
1331
|
+
const resolved = _kubb_kit.ast.ref.mergeSchema(schema);
|
|
1461
1332
|
return resolved.type === "union" && !!getLiteralUnion({
|
|
1462
1333
|
node: resolved,
|
|
1463
1334
|
options
|
|
1464
1335
|
})?.coerce;
|
|
1465
1336
|
});
|
|
1466
1337
|
}
|
|
1338
|
+
function hasPlainDiscriminator({ member, propertyName }) {
|
|
1339
|
+
const schemas = _kubb_kit.ast.schema.findProperties({
|
|
1340
|
+
node: member,
|
|
1341
|
+
propertyName
|
|
1342
|
+
});
|
|
1343
|
+
return schemas.length > 0 && !schemas.some(({ schema }) => _kubb_kit.ast.schema.getLiterals(_kubb_kit.ast.ref.mergeSchema(schema)).length > 0);
|
|
1344
|
+
}
|
|
1467
1345
|
/**
|
|
1468
|
-
* Builds the scalar handlers both dialects share.
|
|
1346
|
+
* Builds the scalar handlers both dialects share. Only a string with a `contentSchema` recurses into a child. The temporal
|
|
1469
1347
|
* scalars and `bigint` differ per dialect, so each printer adds its own.
|
|
1470
1348
|
*/
|
|
1471
1349
|
function buildScalarNodes(dialect) {
|
|
@@ -1478,18 +1356,20 @@ function buildScalarNodes(dialect) {
|
|
|
1478
1356
|
null: () => "z.null()",
|
|
1479
1357
|
string(node) {
|
|
1480
1358
|
const coerce = shouldCoerce(this.options.coercion, "strings");
|
|
1481
|
-
const
|
|
1482
|
-
if (call) return `${call}${dialect.lengths({
|
|
1483
|
-
...node,
|
|
1484
|
-
regexType: this.options.regexType
|
|
1485
|
-
})}`;
|
|
1486
|
-
const base = coerce ? "z.coerce.string()" : "z.string()";
|
|
1359
|
+
const base = (coerce ? void 0 : stringFormatCalls.get(node.format) ?? contentEncodingCalls.get(node.content?.encoding?.toLowerCase())) ?? (coerce ? "z.coerce.string()" : "z.string()");
|
|
1487
1360
|
const pattern = node.pattern ?? integerFormatPattern(node.format);
|
|
1488
|
-
|
|
1361
|
+
const value = `${base}${dialect.lengths({
|
|
1489
1362
|
...node,
|
|
1490
1363
|
pattern,
|
|
1491
1364
|
regexType: this.options.regexType
|
|
1492
1365
|
})}`;
|
|
1366
|
+
const contentNode = _kubb_kit.ast.schema.getEmbeddedJson(node);
|
|
1367
|
+
if (!contentNode) return value;
|
|
1368
|
+
const direction = this.options.direction;
|
|
1369
|
+
this.options.direction = "decode";
|
|
1370
|
+
const contentSchema = this.transform(contentNode);
|
|
1371
|
+
this.options.direction = direction;
|
|
1372
|
+
return contentSchema ? dialect.refine(value, jsonContentCheck(contentSchema)) : value;
|
|
1493
1373
|
},
|
|
1494
1374
|
number(node) {
|
|
1495
1375
|
return `${shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.number()" : "z.number()"}${dialect.numbers(node)}`;
|
|
@@ -1522,7 +1402,7 @@ function buildScalarNodes(dialect) {
|
|
|
1522
1402
|
ipv6: () => "z.ipv6()",
|
|
1523
1403
|
blob: () => "z.instanceof(Blob)",
|
|
1524
1404
|
enum(node) {
|
|
1525
|
-
const nonNullValues =
|
|
1405
|
+
const nonNullValues = _kubb_kit.ast.schema.getLiterals(node).filter((v) => v !== null);
|
|
1526
1406
|
if (node.namedEnumValues?.length) {
|
|
1527
1407
|
const literals = nonNullValues.map((v) => buildLiteral(v));
|
|
1528
1408
|
if (literals.length === 1) return literals[0];
|
|
@@ -1541,11 +1421,12 @@ function buildScalarNodes(dialect) {
|
|
|
1541
1421
|
*/
|
|
1542
1422
|
function buildZodNodes(dialect, cyclicSchemas) {
|
|
1543
1423
|
const unionsWithSiblings = /* @__PURE__ */ new WeakSet();
|
|
1424
|
+
const printNullable = (schema, output) => output && schema.nullable ? dialect.nullable(output) : output;
|
|
1544
1425
|
return {
|
|
1545
1426
|
...buildScalarNodes(dialect),
|
|
1546
1427
|
ref(node) {
|
|
1547
1428
|
if (!node.name) return null;
|
|
1548
|
-
const refName = _kubb_kit.ast.
|
|
1429
|
+
const refName = _kubb_kit.ast.ref.getName(node);
|
|
1549
1430
|
if (!refName) return null;
|
|
1550
1431
|
const useInputVariant = node.ref != null && (dialect.useInputVariant?.(node, this.options) ?? false);
|
|
1551
1432
|
const resolvedName = node.ref ? useInputVariant ? this.options.resolver?.schema.inputName(refName) ?? refName : this.options.resolver?.name(refName) ?? refName : node.name;
|
|
@@ -1560,47 +1441,50 @@ function buildZodNodes(dialect, cyclicSchemas) {
|
|
|
1560
1441
|
const isStrict = node.additionalProperties === false && patterns.length === 0;
|
|
1561
1442
|
const isLoose = node.additionalProperties === true;
|
|
1562
1443
|
const objectBase = `${isStrict ? "z.strictObject" : isLoose ? "z.looseObject" : "z.object"}(${dialect.objectShape(this, node)})`;
|
|
1563
|
-
return
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
const
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
})(),
|
|
1592
|
-
node
|
|
1593
|
-
});
|
|
1444
|
+
return (() => {
|
|
1445
|
+
const recordFn = isClosedKeySchema(propertyNamesNode) ? "z.partialRecord" : "z.record";
|
|
1446
|
+
if (node.additionalProperties && node.additionalProperties !== true) {
|
|
1447
|
+
const catchallType = this.transform(node.additionalProperties);
|
|
1448
|
+
if (entries.length === 0) return catchallType ? `${recordFn}(${propertyNamesKeySchema || "z.string()"}, ${catchallType})` : objectBase;
|
|
1449
|
+
return catchallType ? dialect.catchall(objectBase, catchallType) : objectBase;
|
|
1450
|
+
}
|
|
1451
|
+
if (entries.length === 0 && patterns.length === 0 && propertyNamesKeySchema && !isStrict) {
|
|
1452
|
+
const unknownType = this.transform(_kubb_kit.ast.factory.createSchema({ type: _kubb_kit.ast.schema.types.unknown }));
|
|
1453
|
+
return `${recordFn}(${propertyNamesKeySchema}, ${unknownType})`;
|
|
1454
|
+
}
|
|
1455
|
+
if (node.additionalProperties === true) return objectBase;
|
|
1456
|
+
if (isStrict) return objectBase;
|
|
1457
|
+
if (patterns.length > 0) {
|
|
1458
|
+
const values = patterns.map(([, valueSchema]) => {
|
|
1459
|
+
const valueType = this.transform(valueSchema) ?? this.transform(_kubb_kit.ast.factory.createSchema({ type: _kubb_kit.ast.schema.types.unknown }));
|
|
1460
|
+
return valueSchema.nullable ? dialect.nullable(valueType) : valueType;
|
|
1461
|
+
});
|
|
1462
|
+
const distinct = [...new Set(values)];
|
|
1463
|
+
const value = distinct.length === 1 ? distinct[0] : `z.union([${distinct.join(", ")}])`;
|
|
1464
|
+
if (entries.length > 0) return dialect.catchall(objectBase, value);
|
|
1465
|
+
return `z.record(${dialect.patternKey({
|
|
1466
|
+
patterns: patterns.map(([pattern]) => pattern),
|
|
1467
|
+
regexType: this.options.regexType
|
|
1468
|
+
})}, ${value})`;
|
|
1469
|
+
}
|
|
1470
|
+
return objectBase;
|
|
1471
|
+
})();
|
|
1594
1472
|
},
|
|
1595
1473
|
array(node) {
|
|
1596
|
-
const base = `z.array(${
|
|
1474
|
+
const base = `z.array(${(node.items ?? []).map((item) => printNullable(item, this.transform(item))).filter(Boolean).join(", ") || this.transform(_kubb_kit.ast.factory.createSchema({ type: _kubb_kit.ast.schema.types.unknown }))})${dialect.lengths({
|
|
1597
1475
|
...node,
|
|
1598
1476
|
regexType: this.options.regexType
|
|
1599
1477
|
})}`;
|
|
1600
|
-
|
|
1478
|
+
const unique = node.unique ? dialect.unique(base) : base;
|
|
1479
|
+
const contains = node.contains && containsCheck(this, node.contains);
|
|
1480
|
+
return contains ? dialect.refine(unique, contains) : unique;
|
|
1601
1481
|
},
|
|
1602
1482
|
tuple(node) {
|
|
1603
|
-
|
|
1483
|
+
const items = (node.items ?? []).map((item) => printNullable(item, this.transform(item))).filter(Boolean);
|
|
1484
|
+
const rest = node.rest ? this.transform(node.rest) : void 0;
|
|
1485
|
+
const tuple = `z.tuple(${buildList(items)}${rest ? `, ${rest}` : ""})`;
|
|
1486
|
+
const contains = node.contains && containsCheck(this, node.contains);
|
|
1487
|
+
return contains ? dialect.refine(tuple, contains) : tuple;
|
|
1604
1488
|
},
|
|
1605
1489
|
union(node) {
|
|
1606
1490
|
const literalUnion = getLiteralUnion({
|
|
@@ -1613,24 +1497,30 @@ function buildZodNodes(dialect, cyclicSchemas) {
|
|
|
1613
1497
|
}
|
|
1614
1498
|
const nodeMembers = node.members ?? [];
|
|
1615
1499
|
const { discriminatorPropertyName } = node;
|
|
1616
|
-
const isDiscriminated = !!discriminatorPropertyName && nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemas)
|
|
1500
|
+
const isDiscriminated = !!discriminatorPropertyName && nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemas) && !hasPlainDiscriminator({
|
|
1501
|
+
member: m,
|
|
1502
|
+
propertyName: discriminatorPropertyName
|
|
1503
|
+
})) && !nodeMembers.some((m) => hasCoercedDiscriminator({
|
|
1617
1504
|
member: m,
|
|
1618
1505
|
propertyName: discriminatorPropertyName,
|
|
1619
1506
|
options: this.options
|
|
1620
1507
|
}));
|
|
1621
1508
|
const isXor = node.strategy === "one" && !isDiscriminated && this.options.oneOfType === "xor";
|
|
1622
|
-
const isStrict = node.strategy === "one" && !isXor && !unionsWithSiblings.has(node);
|
|
1623
|
-
const members =
|
|
1509
|
+
const isStrict = node.strategy === "one" && !isXor && !isDiscriminated && !unionsWithSiblings.has(node);
|
|
1510
|
+
const members = (node.members ?? []).map((memberNode) => {
|
|
1511
|
+
const output = this.transform(isStrict ? strictOneOfNode(memberNode) : memberNode);
|
|
1512
|
+
return output && isStrict && dialect.strictMember ? dialect.strictMember(output, memberNode, cyclicSchemas) : output;
|
|
1513
|
+
}).map((output, index) => printNullable(nodeMembers[index], output)).filter(Boolean);
|
|
1624
1514
|
if (members.length === 0) return "";
|
|
1625
1515
|
if (members.length === 1) return members[0];
|
|
1626
|
-
if (isDiscriminated) return `z.discriminatedUnion(${
|
|
1516
|
+
if (isDiscriminated) return `z.discriminatedUnion(${_kubb_kit.text.quote(node.discriminatorPropertyName)}, ${buildList(members)})`;
|
|
1627
1517
|
if (isXor) return `z.xor(${buildList(members)})`;
|
|
1628
1518
|
return `z.union(${buildList(members)})`;
|
|
1629
1519
|
},
|
|
1630
1520
|
intersection(node) {
|
|
1631
1521
|
const members = node.members ?? [];
|
|
1632
1522
|
if (members.length === 0) return "";
|
|
1633
|
-
const hasSiblingProperties = members.some((member) => member.type === _kubb_kit.ast.
|
|
1523
|
+
const hasSiblingProperties = members.some((member) => member.type === _kubb_kit.ast.schema.types.object && member.properties.length > 0);
|
|
1634
1524
|
const transform = (member) => {
|
|
1635
1525
|
if (!hasSiblingProperties) return this.transform(member);
|
|
1636
1526
|
unionsWithSiblings.add(member);
|
|
@@ -1644,7 +1534,18 @@ function buildZodNodes(dialect, cyclicSchemas) {
|
|
|
1644
1534
|
if (!first) return "";
|
|
1645
1535
|
const firstBase = transform(first);
|
|
1646
1536
|
if (!firstBase) return "";
|
|
1647
|
-
if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemas))
|
|
1537
|
+
if (rest.length > 0 && isObjectComposableIntersection(node, cyclicSchemas)) {
|
|
1538
|
+
const discriminator = _kubb_kit.ast.schema.narrow(first, _kubb_kit.ast.schema.types.union)?.discriminatorPropertyName;
|
|
1539
|
+
return preserveRequiredProperties(members).slice(1).reduce((acc, member) => {
|
|
1540
|
+
const object = _kubb_kit.ast.schema.narrow(member, _kubb_kit.ast.schema.types.object);
|
|
1541
|
+
if (!discriminator || !object?.properties.some((property) => property.name === discriminator)) return dialect.extend(acc, dialect.objectShape(this, member));
|
|
1542
|
+
const properties = object.properties.filter((property) => property.name !== discriminator);
|
|
1543
|
+
return properties.length ? dialect.extend(acc, dialect.objectShape(this, {
|
|
1544
|
+
...object,
|
|
1545
|
+
properties
|
|
1546
|
+
})) : acc;
|
|
1547
|
+
}, firstBase);
|
|
1548
|
+
}
|
|
1648
1549
|
return rest.reduce((acc, member) => {
|
|
1649
1550
|
const constraint = getMemberConstraint({
|
|
1650
1551
|
member,
|
|
@@ -1659,15 +1560,57 @@ function buildZodNodes(dialect, cyclicSchemas) {
|
|
|
1659
1560
|
}
|
|
1660
1561
|
};
|
|
1661
1562
|
}
|
|
1563
|
+
function containsCheck(ctx, { schema, min = 1, max }) {
|
|
1564
|
+
if (min === 0 && max === void 0) return null;
|
|
1565
|
+
const countBounds = [`count >= ${min}`, max !== void 0 && `count <= ${max}`].filter(Boolean).join(" && ");
|
|
1566
|
+
const message = max !== void 0 ? `Array must contain ${min} to ${max} matching items` : `Array must contain at least ${min} matching items`;
|
|
1567
|
+
return `(items) => { const count = items.filter((item) => ${ctx.transform(schema) ?? "z.unknown()"}.safeParse(item).success).length; return ${countBounds} }, { message: ${_kubb_kit.text.quote(message)} }`;
|
|
1568
|
+
}
|
|
1569
|
+
function conditionExpression(ctx, schema) {
|
|
1570
|
+
if (!schema) return "true";
|
|
1571
|
+
const object = _kubb_kit.ast.schema.narrow(schema, _kubb_kit.ast.schema.types.object);
|
|
1572
|
+
const keyOnlyNames = object?.properties.filter((property) => property.required && property.schema.type === _kubb_kit.ast.schema.types.unknown).map((property) => property.name) ?? [];
|
|
1573
|
+
const restSchema = object ? {
|
|
1574
|
+
...object,
|
|
1575
|
+
properties: object.properties.filter((property) => !keyOnlyNames.includes(property.name))
|
|
1576
|
+
} : schema;
|
|
1577
|
+
const isOnlyKeys = keyOnlyNames.length > 0 && restSchema.type === "object" && restSchema.properties.length === 0 && restSchema.additionalProperties === void 0 && !restSchema.patternProperties && !restSchema.propertyNames && !hasInputChecks(restSchema);
|
|
1578
|
+
return [keyOnlyNames.length > 0 && `typeof value === "object" && value !== null && ${JSON.stringify(keyOnlyNames)}.every((key) => key in value)`, !isOnlyKeys && `${ctx.transform(restSchema) ?? "z.unknown()"}.safeParse(value).success`].filter(Boolean).join(" && ");
|
|
1579
|
+
}
|
|
1580
|
+
/**
|
|
1581
|
+
* Wraps each handler so its output first checks the raw input, see {@link inputChecks}.
|
|
1582
|
+
*/
|
|
1583
|
+
function withInputChecks(nodes) {
|
|
1584
|
+
const wrapHandler = (type) => {
|
|
1585
|
+
const handler = nodes[type];
|
|
1586
|
+
if (!handler) return [];
|
|
1587
|
+
const checkedHandler = function(node) {
|
|
1588
|
+
const output = handler.call(this, node);
|
|
1589
|
+
if (!output || !hasInputChecks(node)) return output;
|
|
1590
|
+
return inputChecks({
|
|
1591
|
+
value: output,
|
|
1592
|
+
node,
|
|
1593
|
+
keySchema: hasKeyCheck(node) ? this.transform(node.propertyNames) : void 0,
|
|
1594
|
+
conditionals: (node.conditionals ?? []).map((entry) => `z.refine((value) => (${conditionExpression(this, entry.if)} ? ${conditionExpression(this, entry.then)} : ${conditionExpression(this, entry.else)}), { message: "Value does not meet a conditional schema" })`)
|
|
1595
|
+
});
|
|
1596
|
+
};
|
|
1597
|
+
return [[type, checkedHandler]];
|
|
1598
|
+
};
|
|
1599
|
+
return Object.fromEntries(Object.values(_kubb_kit.ast.schema.types).flatMap(wrapHandler));
|
|
1600
|
+
}
|
|
1662
1601
|
// src/printers/printerZod.ts
|
|
1663
1602
|
function strictOneOfMember(member, node, cyclicSchemas) {
|
|
1664
|
-
if (node.type === _kubb_kit.ast.
|
|
1603
|
+
if (node.type === _kubb_kit.ast.schema.types.ref) {
|
|
1665
1604
|
if (member.startsWith("z.lazy(")) return member;
|
|
1666
|
-
const refName = _kubb_kit.ast.
|
|
1605
|
+
const refName = _kubb_kit.ast.ref.getName(node);
|
|
1667
1606
|
if (refName && cyclicSchemas?.has(refName)) return member;
|
|
1668
|
-
const schema =
|
|
1607
|
+
const schema = _kubb_kit.ast.ref.mergeSchema(node);
|
|
1669
1608
|
if (schema.nullable || schema.optional || node.nullable || node.optional) return member;
|
|
1670
|
-
if (schema.type === _kubb_kit.ast.
|
|
1609
|
+
if (schema.type === _kubb_kit.ast.schema.types.object && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) {
|
|
1610
|
+
const checks = [node, node.schema].filter((entry) => entry && hasInputChecks(entry)).length;
|
|
1611
|
+
if (checks === 2) return member;
|
|
1612
|
+
return checks ? `z.pipe(${member}.in, ${member}.out.strict())` : `${member}.strict()`;
|
|
1613
|
+
}
|
|
1671
1614
|
}
|
|
1672
1615
|
return member;
|
|
1673
1616
|
}
|
|
@@ -1683,7 +1626,7 @@ function buildZodObjectShape(ctx, node) {
|
|
|
1683
1626
|
optional: schema.optional || required === false,
|
|
1684
1627
|
nullish: schema.nullish,
|
|
1685
1628
|
defaultValue: meta.default,
|
|
1686
|
-
description: schema.type !== _kubb_kit.ast.
|
|
1629
|
+
description: schema.type !== _kubb_kit.ast.schema.types.ref && meta.type === _kubb_kit.ast.schema.types.ref ? void 0 : meta.description,
|
|
1687
1630
|
examples: meta.examples
|
|
1688
1631
|
});
|
|
1689
1632
|
if (!cyclic) return `${objectKey(name)}: ${body}`;
|
|
@@ -1704,21 +1647,22 @@ function buildZodObjectShape(ctx, node) {
|
|
|
1704
1647
|
function recursiveGetterType({ ctx, schema, required }) {
|
|
1705
1648
|
function baseType(schema) {
|
|
1706
1649
|
if (ctx.options.nodes?.[schema.type]) return void 0;
|
|
1707
|
-
if (schema.type === _kubb_kit.ast.
|
|
1650
|
+
if (schema.type === _kubb_kit.ast.schema.types.ref) {
|
|
1708
1651
|
const saved = ctx.options.cyclicSchemas;
|
|
1709
1652
|
ctx.options.cyclicSchemas = void 0;
|
|
1710
1653
|
const name = ctx.transform(schema);
|
|
1711
1654
|
ctx.options.cyclicSchemas = saved;
|
|
1712
1655
|
return name ? `typeof ${name}` : void 0;
|
|
1713
1656
|
}
|
|
1714
|
-
if (schema.type === _kubb_kit.ast.
|
|
1657
|
+
if (schema.type === _kubb_kit.ast.schema.types.array && schema.items?.length === 1) {
|
|
1715
1658
|
const item = baseType(schema.items[0]);
|
|
1716
|
-
|
|
1659
|
+
if (!item) return void 0;
|
|
1660
|
+
return `z.ZodArray<${schema.items[0].nullable ? `z.ZodNullable<${item}>` : item}>`;
|
|
1717
1661
|
}
|
|
1718
1662
|
}
|
|
1719
1663
|
const base = baseType(schema);
|
|
1720
1664
|
if (!base) return void 0;
|
|
1721
|
-
const meta =
|
|
1665
|
+
const meta = _kubb_kit.ast.ref.mergeSchema(schema);
|
|
1722
1666
|
const optional = schema.optional || required === false;
|
|
1723
1667
|
const wrapped = (() => {
|
|
1724
1668
|
if (schema.nullish || meta.nullable && optional) return `z.ZodOptional<z.ZodNullable<${base}>>`;
|
|
@@ -1733,12 +1677,12 @@ function recursiveGetterType({ ctx, schema, required }) {
|
|
|
1733
1677
|
* `direction` themselves, so {@link containsDirectionalNode} probes their children instead.
|
|
1734
1678
|
*/
|
|
1735
1679
|
const CONTAINER_TYPES = /* @__PURE__ */ new Set([
|
|
1736
|
-
_kubb_kit.ast.
|
|
1737
|
-
_kubb_kit.ast.
|
|
1738
|
-
_kubb_kit.ast.
|
|
1739
|
-
_kubb_kit.ast.
|
|
1740
|
-
_kubb_kit.ast.
|
|
1741
|
-
_kubb_kit.ast.
|
|
1680
|
+
_kubb_kit.ast.schema.types.object,
|
|
1681
|
+
_kubb_kit.ast.schema.types.array,
|
|
1682
|
+
_kubb_kit.ast.schema.types.tuple,
|
|
1683
|
+
_kubb_kit.ast.schema.types.union,
|
|
1684
|
+
_kubb_kit.ast.schema.types.intersection,
|
|
1685
|
+
_kubb_kit.ast.schema.types.ref
|
|
1742
1686
|
]);
|
|
1743
1687
|
/**
|
|
1744
1688
|
* Runs the node's effective handler (a `printer.nodes` override, else the built-in) once per
|
|
@@ -1769,7 +1713,7 @@ function variesByDirection({ node, printerOptions }) {
|
|
|
1769
1713
|
*/
|
|
1770
1714
|
function containsDirectionalNode({ node, printerOptions }) {
|
|
1771
1715
|
if (!node) return false;
|
|
1772
|
-
return _kubb_kit.ast.
|
|
1716
|
+
return _kubb_kit.ast.schema.find(node, {
|
|
1773
1717
|
predicate: (schema) => variesByDirection({
|
|
1774
1718
|
node: schema,
|
|
1775
1719
|
printerOptions
|
|
@@ -1781,10 +1725,10 @@ function containsDirectionalNode({ node, printerOptions }) {
|
|
|
1781
1725
|
* Names of the `$ref` schemas the generator should route to their input (encode) variant.
|
|
1782
1726
|
*/
|
|
1783
1727
|
function collectDirectionalRefNames({ node, printerOptions }) {
|
|
1784
|
-
return _kubb_kit.ast.
|
|
1728
|
+
return _kubb_kit.ast.node.collect(node, { schema: (n) => n.type === _kubb_kit.ast.schema.types.ref && n.ref && containsDirectionalNode({
|
|
1785
1729
|
node: n,
|
|
1786
1730
|
printerOptions
|
|
1787
|
-
}) ? _kubb_kit.ast.
|
|
1731
|
+
}) ? _kubb_kit.ast.ref.getName(n) ?? void 0 : void 0 });
|
|
1788
1732
|
}
|
|
1789
1733
|
/**
|
|
1790
1734
|
* Handlers that never recurse into children, so {@link variesByDirection} can call one directly
|
|
@@ -1806,6 +1750,7 @@ const standardDialect = {
|
|
|
1806
1750
|
coercedNumber: (schema) => `z.coerce.number().pipe(${schema})`,
|
|
1807
1751
|
catchall: (object, value) => `${object}.catchall(${value})`,
|
|
1808
1752
|
nullable: (value) => `${value}.nullable()`,
|
|
1753
|
+
refine: (value, check) => `${value}.refine(${check})`,
|
|
1809
1754
|
unique: (array) => `${array}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`,
|
|
1810
1755
|
extend: (base, shape) => `${base}.extend(${shape})`,
|
|
1811
1756
|
intersection: (left, right) => `${left}.and(${right})`,
|
|
@@ -1817,8 +1762,8 @@ const standardDialect = {
|
|
|
1817
1762
|
};
|
|
1818
1763
|
const scalarNodes = {
|
|
1819
1764
|
...buildScalarNodes(standardDialect),
|
|
1820
|
-
bigint() {
|
|
1821
|
-
return
|
|
1765
|
+
bigint(node) {
|
|
1766
|
+
return `z.coerce.bigint()${numberConstraints(bigintConstraints(node), "n")}`;
|
|
1822
1767
|
},
|
|
1823
1768
|
date(node) {
|
|
1824
1769
|
if (node.representation !== "date") return "z.iso.date()";
|
|
@@ -1851,14 +1796,14 @@ const scalarNodes = {
|
|
|
1851
1796
|
* const code = printer.print(stringNode) // "z.string()"
|
|
1852
1797
|
* ```
|
|
1853
1798
|
*/
|
|
1854
|
-
const printerZod = _kubb_kit.
|
|
1799
|
+
const printerZod = (0, _kubb_kit.createPrinter)((options) => {
|
|
1855
1800
|
return {
|
|
1856
1801
|
name: "zod",
|
|
1857
1802
|
options,
|
|
1858
|
-
nodes: {
|
|
1803
|
+
nodes: withInputChecks({
|
|
1859
1804
|
...buildZodNodes(standardDialect, options.cyclicSchemas),
|
|
1860
1805
|
...scalarNodes
|
|
1861
|
-
},
|
|
1806
|
+
}),
|
|
1862
1807
|
overrides: options.nodes,
|
|
1863
1808
|
print(node) {
|
|
1864
1809
|
return printWithOmit(this, node, (value, meta) => applyModifiers({
|
|
@@ -1915,18 +1860,19 @@ const miniDialect = {
|
|
|
1915
1860
|
coercedNumber: (schema) => `z.pipe(z.coerce.number(), ${schema})`,
|
|
1916
1861
|
catchall: (object, value) => `z.catchall(${object}, ${value})`,
|
|
1917
1862
|
nullable: (value) => `z.nullable(${value})`,
|
|
1863
|
+
refine: (value, check) => `${value}.check(z.refine(${check}))`,
|
|
1918
1864
|
unique: (array) => `${array}.check(z.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" }))`,
|
|
1919
1865
|
extend: (base, shape) => `z.extend(${base}, ${shape})`,
|
|
1920
1866
|
intersection: (left, right) => `z.intersection(${left}, ${right})`
|
|
1921
1867
|
};
|
|
1922
|
-
const printerZodMini = _kubb_kit.
|
|
1868
|
+
const printerZodMini = (0, _kubb_kit.createPrinter)((options) => {
|
|
1923
1869
|
return {
|
|
1924
1870
|
name: "zod-mini",
|
|
1925
1871
|
options,
|
|
1926
|
-
nodes: {
|
|
1872
|
+
nodes: withInputChecks({
|
|
1927
1873
|
...buildZodNodes(miniDialect, options.cyclicSchemas),
|
|
1928
1874
|
bigint(node) {
|
|
1929
|
-
return `z.coerce.bigint()${numberChecksMini(node)}`;
|
|
1875
|
+
return `z.coerce.bigint()${numberChecksMini(bigintConstraints(node), "n")}`;
|
|
1930
1876
|
},
|
|
1931
1877
|
date(node) {
|
|
1932
1878
|
if (node.representation === "string") return "z.iso.date()";
|
|
@@ -1939,7 +1885,7 @@ const printerZodMini = _kubb_kit.ast.createPrinter((options) => {
|
|
|
1939
1885
|
if (node.representation === "string") return "z.iso.time()";
|
|
1940
1886
|
return "z.date()";
|
|
1941
1887
|
}
|
|
1942
|
-
},
|
|
1888
|
+
}),
|
|
1943
1889
|
overrides: options.nodes,
|
|
1944
1890
|
print(node) {
|
|
1945
1891
|
return printWithOmit(this, node, (value, meta) => applyMiniModifiers({
|
|
@@ -2026,7 +1972,8 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2026
1972
|
node,
|
|
2027
1973
|
root,
|
|
2028
1974
|
output,
|
|
2029
|
-
group: group ?? void 0
|
|
1975
|
+
group: group ?? void 0,
|
|
1976
|
+
checks: true
|
|
2030
1977
|
});
|
|
2031
1978
|
const inputImportEntries = hasDirectionalNode ? [...directionalRefNames].map((schemaName) => ({
|
|
2032
1979
|
name: [resolver.schema.inputName(schemaName)],
|
|
@@ -2117,7 +2064,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2117
2064
|
});
|
|
2118
2065
|
},
|
|
2119
2066
|
operation(node, ctx) {
|
|
2120
|
-
if (!_kubb_kit.ast.
|
|
2067
|
+
if (!_kubb_kit.ast.http.isOperation(node)) return null;
|
|
2121
2068
|
const { adapter, config, resolver, root } = ctx;
|
|
2122
2069
|
const { output, coercion, guidType, regexType, oneOfType, compile, mini, inferred, importPath, group, printer } = ctx.options;
|
|
2123
2070
|
const dateType = getOasAdapter(adapter).options.dateType;
|
|
@@ -2146,17 +2093,18 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2146
2093
|
node: schema,
|
|
2147
2094
|
printerOptions
|
|
2148
2095
|
})) : null;
|
|
2096
|
+
const schemaPrinter = getPrinter(resolver, {
|
|
2097
|
+
...printerOptions,
|
|
2098
|
+
keysToOmit
|
|
2099
|
+
}, mini, direction);
|
|
2149
2100
|
const imports = resolver.imports({
|
|
2150
2101
|
node: schema,
|
|
2151
2102
|
root,
|
|
2152
2103
|
output,
|
|
2153
2104
|
group: group ?? void 0,
|
|
2105
|
+
checks: true,
|
|
2154
2106
|
name: (schemaName) => directionalRefNames?.has(schemaName) ? resolver.schema.inputName(schemaName) : resolver.name(schemaName)
|
|
2155
2107
|
});
|
|
2156
|
-
const schemaPrinter = getPrinter(resolver, {
|
|
2157
|
-
...printerOptions,
|
|
2158
|
-
keysToOmit
|
|
2159
|
-
}, mini, direction);
|
|
2160
2108
|
return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
|
|
2161
2109
|
root: meta.file.path,
|
|
2162
2110
|
path: imp.path,
|
|
@@ -2196,7 +2144,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2196
2144
|
})] });
|
|
2197
2145
|
}
|
|
2198
2146
|
function buildResponseUnion({ responses, name, fallbackUnknown }) {
|
|
2199
|
-
if (new Set(
|
|
2147
|
+
if (new Set(_kubb_kit.ast.http.responses.getImportedNames(responses).map((refName) => resolver.name(refName))).has(name)) return null;
|
|
2200
2148
|
const members = responses.map((res) => _kubb_kit.ast.factory.createSchema({
|
|
2201
2149
|
type: "ref",
|
|
2202
2150
|
name: resolver.response.status(node, res.statusCode)
|
|
@@ -2229,14 +2177,27 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2229
2177
|
keysToOmit: primary?.keysToOmit
|
|
2230
2178
|
});
|
|
2231
2179
|
});
|
|
2180
|
+
const toParamsSchema = (params) => _kubb_kit.ast.factory.createSchema({
|
|
2181
|
+
type: "object",
|
|
2182
|
+
primitive: "object",
|
|
2183
|
+
properties: params.map((param) => _kubb_kit.ast.factory.createProperty({
|
|
2184
|
+
name: param.name,
|
|
2185
|
+
required: param.required,
|
|
2186
|
+
schema: param.schema
|
|
2187
|
+
}))
|
|
2188
|
+
});
|
|
2189
|
+
const responseHeaderSchemas = node.responses.map((res) => res.headers?.length ? renderSchemaEntry({
|
|
2190
|
+
schema: toParamsSchema(res.headers),
|
|
2191
|
+
name: resolver.response.headers(node, res.statusCode)
|
|
2192
|
+
}) : null);
|
|
2232
2193
|
const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
|
|
2233
|
-
const successResponsesWithSchema =
|
|
2194
|
+
const successResponsesWithSchema = responsesWithSchema.filter((res) => _kubb_kit.ast.http.status.isSuccess(res));
|
|
2234
2195
|
const responseUnionSchema = responsesWithSchema.length > 0 ? buildResponseUnion({
|
|
2235
2196
|
responses: successResponsesWithSchema,
|
|
2236
2197
|
name: resolver.response.response(node),
|
|
2237
2198
|
fallbackUnknown: true
|
|
2238
2199
|
}) : null;
|
|
2239
|
-
const errorResponsesWithSchema = responsesWithSchema.filter((res) => !
|
|
2200
|
+
const errorResponsesWithSchema = responsesWithSchema.filter((res) => !_kubb_kit.ast.http.status.isSuccess(res));
|
|
2240
2201
|
const errorUnionSchema = errorResponsesWithSchema.length > 0 ? buildResponseUnion({
|
|
2241
2202
|
responses: errorResponsesWithSchema,
|
|
2242
2203
|
name: resolver.response.error(node),
|
|
@@ -2263,7 +2224,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2263
2224
|
description: node.requestBody.description ?? schema.description
|
|
2264
2225
|
}), "encode");
|
|
2265
2226
|
})();
|
|
2266
|
-
const { path, query, header, cookie } =
|
|
2227
|
+
const { path, query, header, cookie } = _kubb_kit.ast.http.groupParameters(node);
|
|
2267
2228
|
const paramGroupSchemas = [
|
|
2268
2229
|
{
|
|
2269
2230
|
kind: "path",
|
|
@@ -2282,15 +2243,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2282
2243
|
params: cookie
|
|
2283
2244
|
}
|
|
2284
2245
|
].filter(({ params }) => params.length > 0).map(({ kind, params }) => renderSchemaEntry({
|
|
2285
|
-
schema:
|
|
2286
|
-
type: "object",
|
|
2287
|
-
primitive: "object",
|
|
2288
|
-
properties: params.map((param) => _kubb_kit.ast.factory.createProperty({
|
|
2289
|
-
name: param.name,
|
|
2290
|
-
required: param.required,
|
|
2291
|
-
schema: param.schema
|
|
2292
|
-
}))
|
|
2293
|
-
}),
|
|
2246
|
+
schema: toParamsSchema(params),
|
|
2294
2247
|
name: resolver.param[kind](node, params[0]),
|
|
2295
2248
|
direction: "encode"
|
|
2296
2249
|
}));
|
|
@@ -2331,6 +2284,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2331
2284
|
}),
|
|
2332
2285
|
paramSchemas,
|
|
2333
2286
|
responseSchemas,
|
|
2287
|
+
responseHeaderSchemas,
|
|
2334
2288
|
responseUnionSchema,
|
|
2335
2289
|
errorUnionSchema,
|
|
2336
2290
|
requestSchema,
|
|
@@ -2343,11 +2297,11 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2343
2297
|
});
|
|
2344
2298
|
// kubb-extension.json
|
|
2345
2299
|
var compatibility = {
|
|
2346
|
-
"kubb": ">=5.
|
|
2300
|
+
"kubb": ">=5.18.4",
|
|
2347
2301
|
"node": ">=22.22"
|
|
2348
2302
|
};
|
|
2349
2303
|
// package.json
|
|
2350
|
-
var version = "5.9.0
|
|
2304
|
+
var version = "5.9.0";
|
|
2351
2305
|
// src/resolvers/resolverZod.ts
|
|
2352
2306
|
/**
|
|
2353
2307
|
* Default resolver used by `@kubb/plugin-zod`. Decides the names and file
|
|
@@ -2367,15 +2321,15 @@ var version = "5.9.0-canary.20261111T122006";
|
|
|
2367
2321
|
const resolverZod = (0, _kubb_kit.createResolver)({
|
|
2368
2322
|
pluginName: "plugin-zod",
|
|
2369
2323
|
name(name) {
|
|
2370
|
-
return this.memo(`name:${name}`, () => ensureValidVarName(
|
|
2324
|
+
return this.memo(`name:${name}`, () => ensureValidVarName(_kubb_kit.casing.camel(name, { suffix: "schema" })));
|
|
2371
2325
|
},
|
|
2372
|
-
file: createCasedFile((part) =>
|
|
2326
|
+
file: createCasedFile((part) => _kubb_kit.casing.camel(part, { suffix: "schema" })),
|
|
2373
2327
|
schema: {
|
|
2374
2328
|
typeName(name) {
|
|
2375
|
-
return this.memo(`typeName:${name}`, () => ensureValidVarName(
|
|
2329
|
+
return this.memo(`typeName:${name}`, () => ensureValidVarName(_kubb_kit.casing.pascal(name, { suffix: "schema type" })));
|
|
2376
2330
|
},
|
|
2377
2331
|
type(name) {
|
|
2378
|
-
return this.memo(`type:${name}`, () => ensureValidVarName(
|
|
2332
|
+
return this.memo(`type:${name}`, () => ensureValidVarName(_kubb_kit.casing.pascal(name, { suffix: "type" })));
|
|
2379
2333
|
},
|
|
2380
2334
|
inputName(name) {
|
|
2381
2335
|
return this.name(`${name} input`);
|
|
@@ -2384,10 +2338,10 @@ const resolverZod = (0, _kubb_kit.createResolver)({
|
|
|
2384
2338
|
return this.schema.typeName(`${name} input`);
|
|
2385
2339
|
},
|
|
2386
2340
|
isName(name) {
|
|
2387
|
-
return ensureValidVarName(
|
|
2341
|
+
return ensureValidVarName(_kubb_kit.casing.camel(name, { prefix: "is" }));
|
|
2388
2342
|
},
|
|
2389
2343
|
assertName(name) {
|
|
2390
|
-
return ensureValidVarName(
|
|
2344
|
+
return ensureValidVarName(_kubb_kit.casing.camel(name, { prefix: "assert" }));
|
|
2391
2345
|
}
|
|
2392
2346
|
},
|
|
2393
2347
|
param: createOperationParamResolver(),
|
|
@@ -2461,7 +2415,7 @@ const pluginZod = (0, _kubb_kit.defineExtension)({
|
|
|
2461
2415
|
exclude,
|
|
2462
2416
|
include,
|
|
2463
2417
|
override,
|
|
2464
|
-
group:
|
|
2418
|
+
group: group ?? null,
|
|
2465
2419
|
importPath,
|
|
2466
2420
|
coercion,
|
|
2467
2421
|
inferred: inferred === "direction" ? "codec" : inferred,
|