@kubb/plugin-zod 5.9.0-canary.20261111T115959 → 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 +763 -807
- 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 +764 -808
- 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,
|
|
@@ -318,437 +538,100 @@ function buildOptionsSchema(node, resolver, { pairContentTypes = false } = {}) {
|
|
|
318
538
|
schema: _kubb_kit.ast.factory.createSchema({
|
|
319
539
|
type: "union",
|
|
320
540
|
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;
|
|
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
|
/**
|
|
@@ -756,6 +639,9 @@ function toFilePath(name, caseLast = _kubb_kit.camelCase) {
|
|
|
756
639
|
* it is, so two of them never share an allocation even when a custom resolver gives both the same
|
|
757
640
|
* name. Leave `scope` empty for the operation's own declaration.
|
|
758
641
|
*
|
|
642
|
+
* The key starts from the operation's input `pointer`, which macros keep, so a plugin whose macro
|
|
643
|
+
* rewrites the method or path still resolves another plugin's names for the same operation.
|
|
644
|
+
*
|
|
759
645
|
* @example
|
|
760
646
|
* ```ts
|
|
761
647
|
* resolver.resolveName({ key: operationKey(node, 'response', 'status', 404), name })
|
|
@@ -764,8 +650,7 @@ function toFilePath(name, caseLast = _kubb_kit.camelCase) {
|
|
|
764
650
|
function operationKey(node, ...scope) {
|
|
765
651
|
return [
|
|
766
652
|
"operation",
|
|
767
|
-
node.method,
|
|
768
|
-
node.path,
|
|
653
|
+
...node.pointer ? [node.pointer] : [node.method, node.path],
|
|
769
654
|
...scope.map(String)
|
|
770
655
|
];
|
|
771
656
|
}
|
|
@@ -836,6 +721,13 @@ function createOperationResponseResolver() {
|
|
|
836
721
|
name
|
|
837
722
|
});
|
|
838
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
|
+
},
|
|
839
731
|
body(node) {
|
|
840
732
|
const name = this.name(`${node.operationId} Body`);
|
|
841
733
|
return this.resolveName({
|
|
@@ -877,75 +769,7 @@ function createCasedFile(caseLast) {
|
|
|
877
769
|
* Names of the schemas in a circular reference chain, computed once per build.
|
|
878
770
|
*/
|
|
879
771
|
function getCircularNames(ctx) {
|
|
880
|
-
return ctx.memo("circularNames:set", () => _kubb_kit.ast.
|
|
881
|
-
}
|
|
882
|
-
// ../../internals/shared/src/group.ts
|
|
883
|
-
/**
|
|
884
|
-
* Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
|
|
885
|
-
* shared default naming so every plugin groups output consistently:
|
|
886
|
-
*
|
|
887
|
-
* - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
|
|
888
|
-
* - other groups use the camelCased group (`pet store` → `petStore`).
|
|
889
|
-
*
|
|
890
|
-
* A user-provided `group.name` always wins over the default namer, so callers stay in
|
|
891
|
-
* control of their output folders. Returns `null` when grouping is disabled, matching the
|
|
892
|
-
* per-plugin convention.
|
|
893
|
-
*
|
|
894
|
-
* @param group - The user-supplied group option, or `undefined` to disable grouping.
|
|
895
|
-
*
|
|
896
|
-
* @example
|
|
897
|
-
* ```ts
|
|
898
|
-
* createGroupConfig(group) // shared across every plugin
|
|
899
|
-
* ```
|
|
900
|
-
*/
|
|
901
|
-
function createGroupConfig(group) {
|
|
902
|
-
if (!group) return null;
|
|
903
|
-
const defaultName = (ctx) => {
|
|
904
|
-
if (group.type === "path") return `${ctx.group.split("/")[1]}`;
|
|
905
|
-
return (0, _kubb_kit.camelCase)(ctx.group);
|
|
906
|
-
};
|
|
907
|
-
return {
|
|
908
|
-
...group,
|
|
909
|
-
name: group.name ? group.name : defaultName
|
|
910
|
-
};
|
|
911
|
-
}
|
|
912
|
-
// ../../internals/shared/src/schemaTraversal.ts
|
|
913
|
-
/**
|
|
914
|
-
* Maps each property of an object schema to its transformed output. Pairs every result with the
|
|
915
|
-
* original property so the printer keeps full control over modifiers, getters, and key syntax.
|
|
916
|
-
*
|
|
917
|
-
* @example
|
|
918
|
-
* ```ts
|
|
919
|
-
* const entries = mapSchemaProperties(node, (schema) => this.transform(schema))
|
|
920
|
-
* // entries: [{ name: 'id', property, output: 'z.number()' }, ...]
|
|
921
|
-
* ```
|
|
922
|
-
*/
|
|
923
|
-
function mapSchemaProperties(node, transform) {
|
|
924
|
-
return node.properties.map((property) => ({
|
|
925
|
-
name: property.name,
|
|
926
|
-
property,
|
|
927
|
-
output: transform(property.schema)
|
|
928
|
-
}));
|
|
929
|
-
}
|
|
930
|
-
/**
|
|
931
|
-
* Maps each member of a union or intersection schema to its transformed output, pairing every
|
|
932
|
-
* result with the original member.
|
|
933
|
-
*/
|
|
934
|
-
function mapSchemaMembers(node, transform) {
|
|
935
|
-
return (node.members ?? []).map((schema) => ({
|
|
936
|
-
schema,
|
|
937
|
-
output: transform(schema)
|
|
938
|
-
}));
|
|
939
|
-
}
|
|
940
|
-
/**
|
|
941
|
-
* Maps each item of an array or tuple schema to its transformed output, pairing every result with
|
|
942
|
-
* the original item.
|
|
943
|
-
*/
|
|
944
|
-
function mapSchemaItems(node, transform) {
|
|
945
|
-
return (node.items ?? []).map((schema) => ({
|
|
946
|
-
schema,
|
|
947
|
-
output: transform(schema)
|
|
948
|
-
}));
|
|
772
|
+
return ctx.memo("circularNames:set", () => _kubb_kit.ast.graph.getCircularNames(ctx.driver.inputNode?.schemas ?? []));
|
|
949
773
|
}
|
|
950
774
|
// src/components/Zod.tsx
|
|
951
775
|
function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuards, isName, assertName, mini, cyclic, compile }) {
|
|
@@ -953,9 +777,9 @@ function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuar
|
|
|
953
777
|
const printerImports = printer.drainImports();
|
|
954
778
|
if (!output) return;
|
|
955
779
|
const needsAnnotation = cyclic && node.type !== "object";
|
|
956
|
-
const
|
|
957
|
-
const isCyclic = Boolean(cyclic || printer.options.cyclicSchemas &&
|
|
958
|
-
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;
|
|
959
783
|
const targetType = inferTypeName ?? `z.infer<typeof ${name}>`;
|
|
960
784
|
const shouldGenerateIs = isName && (typeof typeGuards === "object" ? typeGuards.is ?? true : Boolean(typeGuards));
|
|
961
785
|
const shouldGenerateAssert = assertName && (typeof typeGuards === "object" ? typeGuards.assert ?? true : Boolean(typeGuards));
|
|
@@ -978,7 +802,7 @@ function Zod({ name, node, printer, inferTypeName, inferKind = "infer", typeGuar
|
|
|
978
802
|
children: /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.Const, {
|
|
979
803
|
export: true,
|
|
980
804
|
name,
|
|
981
|
-
type: needsAnnotation ? "z.ZodType" : void 0,
|
|
805
|
+
type: needsAnnotation ? mini ? "z.ZodMiniType" : "z.ZodType" : void 0,
|
|
982
806
|
children: value
|
|
983
807
|
})
|
|
984
808
|
}),
|
|
@@ -1038,7 +862,7 @@ function shouldCoerce(coercion, type) {
|
|
|
1038
862
|
*/
|
|
1039
863
|
function isClosedKeySchema(node) {
|
|
1040
864
|
if (!node) return false;
|
|
1041
|
-
const resolved =
|
|
865
|
+
const resolved = _kubb_kit.ast.ref.mergeSchema(node);
|
|
1042
866
|
if (resolved.type === "enum") return true;
|
|
1043
867
|
if (resolved.type === "union") {
|
|
1044
868
|
const members = resolved.members ?? [];
|
|
@@ -1048,12 +872,12 @@ function isClosedKeySchema(node) {
|
|
|
1048
872
|
}
|
|
1049
873
|
/**
|
|
1050
874
|
* Whether the node is a plain inline object whose shape can be lifted into an `.extend({ … })`
|
|
1051
|
-
* argument.
|
|
875
|
+
* argument. Input checks, a catchall, `patternProperties`, or a nullable/optional wrapper cannot, so those stay
|
|
1052
876
|
* on `.and(…)`.
|
|
1053
877
|
*/
|
|
1054
878
|
function isPlainInlineObject(node) {
|
|
1055
879
|
if (node.type !== "object") return false;
|
|
1056
|
-
if (node
|
|
880
|
+
if (hasInputChecks(node)) return false;
|
|
1057
881
|
if (node.nullable || node.optional || node.nullish) return false;
|
|
1058
882
|
const hasAdditionalProperties = node.additionalProperties !== void 0;
|
|
1059
883
|
const hasPatternProperties = Boolean(node.patternProperties);
|
|
@@ -1064,21 +888,21 @@ function isPlainInlineObject(node) {
|
|
|
1064
888
|
* `z.discriminatedUnion` option. Covers object nodes, `$ref`s that resolve to (or are still
|
|
1065
889
|
* unresolved) objects, single-member unions of an object, and object-composable `allOf`.
|
|
1066
890
|
*
|
|
1067
|
-
*
|
|
891
|
+
* Input checks, such as property counts and `conditionals`, render as a pipe. A ref into `cyclicSchemas` renders as `z.lazy(…)`.
|
|
1068
892
|
*/
|
|
1069
893
|
function isObjectSchemaNode(node, cyclicSchemas) {
|
|
1070
894
|
if (node.nullable || node.optional || node.nullish) return false;
|
|
895
|
+
if (hasInputChecks(node)) return false;
|
|
1071
896
|
if (node.type === "object") {
|
|
1072
|
-
if (node.minProperties !== void 0 || node.maxProperties !== void 0) return false;
|
|
1073
897
|
const hasProperties = Boolean(node.properties?.length);
|
|
1074
898
|
const hasTypedAdditionalProperties = Boolean(node.additionalProperties && node.additionalProperties !== true);
|
|
1075
899
|
const hasPatternProperties = Boolean(node.patternProperties);
|
|
1076
900
|
return !(!hasProperties && (hasTypedAdditionalProperties || hasPatternProperties || node.propertyNames));
|
|
1077
901
|
}
|
|
1078
902
|
if (node.type === "ref") {
|
|
1079
|
-
const refName = _kubb_kit.ast.
|
|
903
|
+
const refName = _kubb_kit.ast.ref.getName(node);
|
|
1080
904
|
if (refName && cyclicSchemas?.has(refName)) return false;
|
|
1081
|
-
const resolved =
|
|
905
|
+
const resolved = _kubb_kit.ast.ref.mergeSchema(node);
|
|
1082
906
|
return resolved.type === "ref" || isObjectSchemaNode(resolved, cyclicSchemas);
|
|
1083
907
|
}
|
|
1084
908
|
if (node.type === "union") {
|
|
@@ -1101,23 +925,23 @@ function preserveRequiredProperties(members) {
|
|
|
1101
925
|
const hasRequiredProperty = ({ node, propertyName, seen = /* @__PURE__ */ new Set() }) => {
|
|
1102
926
|
if (seen.has(node)) return false;
|
|
1103
927
|
seen.add(node);
|
|
1104
|
-
const resolved =
|
|
1105
|
-
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);
|
|
1106
930
|
if (composition) {
|
|
1107
|
-
if (composition.type === _kubb_kit.ast.
|
|
931
|
+
if (composition.type === _kubb_kit.ast.schema.types.union && composition.members?.length !== 1) return false;
|
|
1108
932
|
return composition.members?.some((child) => hasRequiredProperty({
|
|
1109
933
|
node: child,
|
|
1110
934
|
propertyName,
|
|
1111
935
|
seen
|
|
1112
936
|
})) ?? false;
|
|
1113
937
|
}
|
|
1114
|
-
return _kubb_kit.ast.
|
|
938
|
+
return _kubb_kit.ast.schema.findProperties({
|
|
1115
939
|
node: resolved,
|
|
1116
940
|
propertyName
|
|
1117
941
|
}).some((property) => property.required !== false && !property.schema.optional && !property.schema.nullish);
|
|
1118
942
|
};
|
|
1119
943
|
return members.map((member, index) => {
|
|
1120
|
-
const object = _kubb_kit.ast.
|
|
944
|
+
const object = _kubb_kit.ast.schema.narrow(member, _kubb_kit.ast.schema.types.object);
|
|
1121
945
|
if (!object || index === 0) return member;
|
|
1122
946
|
return {
|
|
1123
947
|
...object,
|
|
@@ -1146,7 +970,7 @@ function preserveRequiredProperties(members) {
|
|
|
1146
970
|
* representation, strings are quoted.
|
|
1147
971
|
*/
|
|
1148
972
|
function formatDefault(value) {
|
|
1149
|
-
if (typeof value === "string") return
|
|
973
|
+
if (typeof value === "string") return _kubb_kit.text.quote(value);
|
|
1150
974
|
if (typeof value === "object" && value !== null) return JSON.stringify(value);
|
|
1151
975
|
return String(value ?? "");
|
|
1152
976
|
}
|
|
@@ -1161,16 +985,17 @@ function formatDefault(value) {
|
|
|
1161
985
|
*/
|
|
1162
986
|
function defaultLiteral(node, value) {
|
|
1163
987
|
if (value === null) return null;
|
|
1164
|
-
const resolved = node ?
|
|
1165
|
-
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")) {
|
|
1166
991
|
if (typeof value === "bigint") return `BigInt(${value})`;
|
|
1167
992
|
if (typeof value === "number" && Number.isInteger(value)) return `BigInt(${value})`;
|
|
1168
993
|
return null;
|
|
1169
994
|
}
|
|
1170
|
-
if (resolved && _kubb_kit.ast.
|
|
1171
|
-
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;
|
|
1172
997
|
if (enumNode) {
|
|
1173
|
-
const values =
|
|
998
|
+
const values = _kubb_kit.ast.schema.getLiterals(enumNode);
|
|
1174
999
|
if (values.length) {
|
|
1175
1000
|
const match = values.find((member) => member === value || String(member) === String(value));
|
|
1176
1001
|
return match != null ? formatDefault(match) : null;
|
|
@@ -1239,13 +1064,13 @@ function patternKeySchemaMini({ patterns, regexType }) {
|
|
|
1239
1064
|
* Build `.min()` / `.max()` / `.gt()` / `.lt()` constraint chains for numbers
|
|
1240
1065
|
* using the standard chainable Zod v4 API.
|
|
1241
1066
|
*/
|
|
1242
|
-
function numberConstraints({ min, max,
|
|
1067
|
+
function numberConstraints({ min, max, exclusive, multipleOf }, suffix = "") {
|
|
1243
1068
|
return [
|
|
1244
|
-
min !== void 0 ? `.min(${min})` : "",
|
|
1245
|
-
max !== void 0 ? `.max(${max})` : "",
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
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})` : ""
|
|
1249
1074
|
].join("");
|
|
1250
1075
|
}
|
|
1251
1076
|
/**
|
|
@@ -1260,36 +1085,71 @@ function lengthConstraints({ min, max, pattern, regexType }) {
|
|
|
1260
1085
|
].join("");
|
|
1261
1086
|
}
|
|
1262
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
|
+
/**
|
|
1263
1102
|
* Build `.check(z.minimum(), z.maximum())` for `zod/mini` numeric constraints.
|
|
1264
1103
|
*/
|
|
1265
|
-
function numberChecksMini({ min, max,
|
|
1266
|
-
const checks = [
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
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);
|
|
1272
1112
|
return checks.length ? `.check(${checks.join(", ")})` : "";
|
|
1273
1113
|
}
|
|
1274
1114
|
/**
|
|
1275
1115
|
* Build `.check(z.minLength(), z.maxLength(), z.regex())` for `zod/mini` length constraints.
|
|
1276
1116
|
*/
|
|
1277
1117
|
function lengthChecksMini({ min, max, pattern, regexType }) {
|
|
1278
|
-
const checks = [
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
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);
|
|
1282
1123
|
return checks.length ? `.check(${checks.join(", ")})` : "";
|
|
1283
1124
|
}
|
|
1284
1125
|
/**
|
|
1285
|
-
*
|
|
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}.
|
|
1286
1136
|
*/
|
|
1287
|
-
function
|
|
1288
|
-
|
|
1289
|
-
|
|
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 }) {
|
|
1290
1145
|
const skip = "typeof value !== \"object\" || value === null || Array.isArray(value)";
|
|
1291
|
-
|
|
1292
|
-
|
|
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);
|
|
1293
1153
|
return checks.length ? `z.pipe(z.unknown().check(${checks.join(", ")}), ${value})` : value;
|
|
1294
1154
|
}
|
|
1295
1155
|
/**
|
|
@@ -1305,8 +1165,8 @@ function applyModifiers({ value, schema, nullable, optional, nullish, defaultVal
|
|
|
1305
1165
|
})();
|
|
1306
1166
|
const literal = defaultValue !== void 0 ? defaultLiteral(schema, defaultValue) : null;
|
|
1307
1167
|
const withDefault = literal !== null ? `${withModifier}.default(${literal})` : withModifier;
|
|
1308
|
-
const withDescription = description ? `${withDefault}.describe(${
|
|
1309
|
-
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;
|
|
1310
1170
|
}
|
|
1311
1171
|
function modifierDepth(schema) {
|
|
1312
1172
|
if (schema.nullish || schema.nullable && schema.optional) return 2;
|
|
@@ -1324,7 +1184,7 @@ function modifierDepth(schema) {
|
|
|
1324
1184
|
* `.unwrap()`, so its printer passes `._zod.def.innerType` as the `step`.
|
|
1325
1185
|
*/
|
|
1326
1186
|
function omitUnwrapChain(node, step = ".unwrap()") {
|
|
1327
|
-
const ref = _kubb_kit.ast.
|
|
1187
|
+
const ref = _kubb_kit.ast.schema.narrow(node, "ref");
|
|
1328
1188
|
if (!ref) return "";
|
|
1329
1189
|
const target = ref.schema ?? ref;
|
|
1330
1190
|
const depth = modifierDepth(target) + (target.default !== void 0 ? 1 : 0);
|
|
@@ -1346,7 +1206,7 @@ function applyMiniModifiers({ value, schema, nullable, optional, nullish, defaul
|
|
|
1346
1206
|
/**
|
|
1347
1207
|
* Calls for string `format`s with a dedicated Zod constructor; a `Map` so `constructor` cannot hit `Object.prototype`.
|
|
1348
1208
|
*/
|
|
1349
|
-
const
|
|
1209
|
+
const stringFormatCalls = /* @__PURE__ */ new Map([
|
|
1350
1210
|
["byte", "z.base64()"],
|
|
1351
1211
|
["base64", "z.base64()"],
|
|
1352
1212
|
["base64url", "z.base64url()"],
|
|
@@ -1357,6 +1217,16 @@ const STRING_FORMAT_CALLS = /* @__PURE__ */ new Map([
|
|
|
1357
1217
|
["hostname", "z.hostname()"]
|
|
1358
1218
|
]);
|
|
1359
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
|
+
/**
|
|
1360
1230
|
* Marks an inline `oneOf` object strict, so `object()` builds `z.strictObject(...)` and no eager `.shape` read hits a deferred getter.
|
|
1361
1231
|
*/
|
|
1362
1232
|
function strictOneOfNode(node) {
|
|
@@ -1369,13 +1239,14 @@ function strictOneOfNode(node) {
|
|
|
1369
1239
|
* The constraint chain an `allOf` member adds to a bare string, number or array; each printer passes its own renderers.
|
|
1370
1240
|
*/
|
|
1371
1241
|
function getMemberConstraint({ member, regexType, lengths, numbers }) {
|
|
1242
|
+
if (hasInputChecks(member) || _kubb_kit.ast.schema.narrow(member, "array")?.contains) return void 0;
|
|
1372
1243
|
if (member.primitive === "string") return lengths({
|
|
1373
|
-
..._kubb_kit.ast.
|
|
1244
|
+
..._kubb_kit.ast.schema.narrow(member, "string") ?? {},
|
|
1374
1245
|
regexType
|
|
1375
1246
|
}) || void 0;
|
|
1376
|
-
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;
|
|
1377
1248
|
if (member.primitive === "array") return lengths({
|
|
1378
|
-
..._kubb_kit.ast.
|
|
1249
|
+
..._kubb_kit.ast.schema.narrow(member, "array") ?? {},
|
|
1379
1250
|
regexType
|
|
1380
1251
|
}) || void 0;
|
|
1381
1252
|
}
|
|
@@ -1383,24 +1254,26 @@ function getMemberConstraint({ member, regexType, lengths, numbers }) {
|
|
|
1383
1254
|
* Builds the `{ key: value, … }` shape shared by the `z.object(...)` and `.extend(...)` renderings.
|
|
1384
1255
|
*/
|
|
1385
1256
|
function buildObjectShape(ctx, node, renderProperty) {
|
|
1386
|
-
const objectNode = _kubb_kit.ast.
|
|
1257
|
+
const objectNode = _kubb_kit.ast.schema.narrow(node, "object");
|
|
1387
1258
|
if (!objectNode) return "{}";
|
|
1388
|
-
const isCyclic = (schema) => ctx.options.cyclicSchemas != null &&
|
|
1389
|
-
|
|
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 }) => {
|
|
1390
1261
|
const hasSelfRef = isCyclic(schema);
|
|
1391
1262
|
const savedCyclicSchemas = ctx.options.cyclicSchemas;
|
|
1392
1263
|
if (hasSelfRef) ctx.options.cyclicSchemas = void 0;
|
|
1393
1264
|
const baseOutput = ctx.transform(schema) ?? ctx.transform(_kubb_kit.ast.factory.createSchema({ type: "unknown" }));
|
|
1394
1265
|
if (hasSelfRef) ctx.options.cyclicSchemas = savedCyclicSchemas;
|
|
1395
1266
|
return baseOutput;
|
|
1396
|
-
})
|
|
1397
|
-
|
|
1267
|
+
});
|
|
1268
|
+
return buildObject(objectNode.properties.map((property, index) => {
|
|
1269
|
+
const { name, schema } = property;
|
|
1270
|
+
const value = transformed[index];
|
|
1398
1271
|
return renderProperty({
|
|
1399
1272
|
name,
|
|
1400
1273
|
value,
|
|
1401
1274
|
schema,
|
|
1402
1275
|
required: property.required,
|
|
1403
|
-
meta:
|
|
1276
|
+
meta: _kubb_kit.ast.ref.mergeSchema(schema),
|
|
1404
1277
|
cyclic: isCyclic(schema)
|
|
1405
1278
|
});
|
|
1406
1279
|
}));
|
|
@@ -1412,14 +1285,14 @@ function printWithOmit(ctx, node, modify, mini = false) {
|
|
|
1412
1285
|
const { keysToOmit } = ctx.options;
|
|
1413
1286
|
const transformed = ctx.transform(node);
|
|
1414
1287
|
if (!transformed) return null;
|
|
1415
|
-
const meta =
|
|
1288
|
+
const meta = _kubb_kit.ast.ref.mergeSchema(node);
|
|
1416
1289
|
return modify((() => {
|
|
1417
1290
|
if (!keysToOmit?.length || meta.primitive !== "object" || meta.type === "union" && meta.discriminatorPropertyName) return transformed;
|
|
1418
1291
|
const unwrap = omitUnwrapChain(node, mini ? "._zod.def.innerType" : ".unwrap()");
|
|
1419
1292
|
const mask = `{ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }`;
|
|
1420
1293
|
const omit = (target) => {
|
|
1421
1294
|
const inner = `${target}${unwrap}`;
|
|
1422
|
-
const hasObjectConstraints =
|
|
1295
|
+
const hasObjectConstraints = hasInputChecks(meta);
|
|
1423
1296
|
const pipe = mini ? `${inner}._zod.def` : inner;
|
|
1424
1297
|
const object = hasObjectConstraints ? `${pipe}.out` : inner;
|
|
1425
1298
|
const omitted = mini ? `z.omit(${object}, ${mask})` : `${object}.omit(${mask})`;
|
|
@@ -1438,7 +1311,7 @@ function printWithOmit(ctx, node, modify, mini = false) {
|
|
|
1438
1311
|
function getLiteralUnion({ node, options }) {
|
|
1439
1312
|
if (node.strategy === "one" && options.oneOfType === "xor" || options.nodes?.enum) return void 0;
|
|
1440
1313
|
const values = (node.members ?? []).map((member) => {
|
|
1441
|
-
const enumNode = _kubb_kit.ast.
|
|
1314
|
+
const enumNode = _kubb_kit.ast.schema.narrow(member, _kubb_kit.ast.schema.types.enum);
|
|
1442
1315
|
return enumNode && !enumNode.name && !enumNode.nullable && !enumNode.namedEnumValues && enumNode.enumValues?.length === 1 ? enumNode.enumValues[0] : void 0;
|
|
1443
1316
|
});
|
|
1444
1317
|
if (!values.length || !values.every((value) => value !== void 0 && value !== null)) return void 0;
|
|
@@ -1451,19 +1324,26 @@ function getLiteralUnion({ node, options }) {
|
|
|
1451
1324
|
* Whether a discriminated union member's discriminator property prints as a coerced literal union.
|
|
1452
1325
|
*/
|
|
1453
1326
|
function hasCoercedDiscriminator({ member, propertyName, options }) {
|
|
1454
|
-
return _kubb_kit.ast.
|
|
1327
|
+
return _kubb_kit.ast.schema.findProperties({
|
|
1455
1328
|
node: member,
|
|
1456
1329
|
propertyName
|
|
1457
1330
|
}).some(({ schema }) => {
|
|
1458
|
-
const resolved =
|
|
1331
|
+
const resolved = _kubb_kit.ast.ref.mergeSchema(schema);
|
|
1459
1332
|
return resolved.type === "union" && !!getLiteralUnion({
|
|
1460
1333
|
node: resolved,
|
|
1461
1334
|
options
|
|
1462
1335
|
})?.coerce;
|
|
1463
1336
|
});
|
|
1464
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
|
+
}
|
|
1465
1345
|
/**
|
|
1466
|
-
* 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
|
|
1467
1347
|
* scalars and `bigint` differ per dialect, so each printer adds its own.
|
|
1468
1348
|
*/
|
|
1469
1349
|
function buildScalarNodes(dialect) {
|
|
@@ -1476,18 +1356,20 @@ function buildScalarNodes(dialect) {
|
|
|
1476
1356
|
null: () => "z.null()",
|
|
1477
1357
|
string(node) {
|
|
1478
1358
|
const coerce = shouldCoerce(this.options.coercion, "strings");
|
|
1479
|
-
const
|
|
1480
|
-
if (call) return `${call}${dialect.lengths({
|
|
1481
|
-
...node,
|
|
1482
|
-
regexType: this.options.regexType
|
|
1483
|
-
})}`;
|
|
1484
|
-
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()");
|
|
1485
1360
|
const pattern = node.pattern ?? integerFormatPattern(node.format);
|
|
1486
|
-
|
|
1361
|
+
const value = `${base}${dialect.lengths({
|
|
1487
1362
|
...node,
|
|
1488
1363
|
pattern,
|
|
1489
1364
|
regexType: this.options.regexType
|
|
1490
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;
|
|
1491
1373
|
},
|
|
1492
1374
|
number(node) {
|
|
1493
1375
|
return `${shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.number()" : "z.number()"}${dialect.numbers(node)}`;
|
|
@@ -1520,7 +1402,7 @@ function buildScalarNodes(dialect) {
|
|
|
1520
1402
|
ipv6: () => "z.ipv6()",
|
|
1521
1403
|
blob: () => "z.instanceof(Blob)",
|
|
1522
1404
|
enum(node) {
|
|
1523
|
-
const nonNullValues =
|
|
1405
|
+
const nonNullValues = _kubb_kit.ast.schema.getLiterals(node).filter((v) => v !== null);
|
|
1524
1406
|
if (node.namedEnumValues?.length) {
|
|
1525
1407
|
const literals = nonNullValues.map((v) => buildLiteral(v));
|
|
1526
1408
|
if (literals.length === 1) return literals[0];
|
|
@@ -1539,11 +1421,12 @@ function buildScalarNodes(dialect) {
|
|
|
1539
1421
|
*/
|
|
1540
1422
|
function buildZodNodes(dialect, cyclicSchemas) {
|
|
1541
1423
|
const unionsWithSiblings = /* @__PURE__ */ new WeakSet();
|
|
1424
|
+
const printNullable = (schema, output) => output && schema.nullable ? dialect.nullable(output) : output;
|
|
1542
1425
|
return {
|
|
1543
1426
|
...buildScalarNodes(dialect),
|
|
1544
1427
|
ref(node) {
|
|
1545
1428
|
if (!node.name) return null;
|
|
1546
|
-
const refName = _kubb_kit.ast.
|
|
1429
|
+
const refName = _kubb_kit.ast.ref.getName(node);
|
|
1547
1430
|
if (!refName) return null;
|
|
1548
1431
|
const useInputVariant = node.ref != null && (dialect.useInputVariant?.(node, this.options) ?? false);
|
|
1549
1432
|
const resolvedName = node.ref ? useInputVariant ? this.options.resolver?.schema.inputName(refName) ?? refName : this.options.resolver?.name(refName) ?? refName : node.name;
|
|
@@ -1558,47 +1441,50 @@ function buildZodNodes(dialect, cyclicSchemas) {
|
|
|
1558
1441
|
const isStrict = node.additionalProperties === false && patterns.length === 0;
|
|
1559
1442
|
const isLoose = node.additionalProperties === true;
|
|
1560
1443
|
const objectBase = `${isStrict ? "z.strictObject" : isLoose ? "z.looseObject" : "z.object"}(${dialect.objectShape(this, node)})`;
|
|
1561
|
-
return
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
const
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
})(),
|
|
1590
|
-
node
|
|
1591
|
-
});
|
|
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
|
+
})();
|
|
1592
1472
|
},
|
|
1593
1473
|
array(node) {
|
|
1594
|
-
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({
|
|
1595
1475
|
...node,
|
|
1596
1476
|
regexType: this.options.regexType
|
|
1597
1477
|
})}`;
|
|
1598
|
-
|
|
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;
|
|
1599
1481
|
},
|
|
1600
1482
|
tuple(node) {
|
|
1601
|
-
|
|
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;
|
|
1602
1488
|
},
|
|
1603
1489
|
union(node) {
|
|
1604
1490
|
const literalUnion = getLiteralUnion({
|
|
@@ -1611,24 +1497,30 @@ function buildZodNodes(dialect, cyclicSchemas) {
|
|
|
1611
1497
|
}
|
|
1612
1498
|
const nodeMembers = node.members ?? [];
|
|
1613
1499
|
const { discriminatorPropertyName } = node;
|
|
1614
|
-
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({
|
|
1615
1504
|
member: m,
|
|
1616
1505
|
propertyName: discriminatorPropertyName,
|
|
1617
1506
|
options: this.options
|
|
1618
1507
|
}));
|
|
1619
1508
|
const isXor = node.strategy === "one" && !isDiscriminated && this.options.oneOfType === "xor";
|
|
1620
|
-
const isStrict = node.strategy === "one" && !isXor && !unionsWithSiblings.has(node);
|
|
1621
|
-
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);
|
|
1622
1514
|
if (members.length === 0) return "";
|
|
1623
1515
|
if (members.length === 1) return members[0];
|
|
1624
|
-
if (isDiscriminated) return `z.discriminatedUnion(${
|
|
1516
|
+
if (isDiscriminated) return `z.discriminatedUnion(${_kubb_kit.text.quote(node.discriminatorPropertyName)}, ${buildList(members)})`;
|
|
1625
1517
|
if (isXor) return `z.xor(${buildList(members)})`;
|
|
1626
1518
|
return `z.union(${buildList(members)})`;
|
|
1627
1519
|
},
|
|
1628
1520
|
intersection(node) {
|
|
1629
1521
|
const members = node.members ?? [];
|
|
1630
1522
|
if (members.length === 0) return "";
|
|
1631
|
-
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);
|
|
1632
1524
|
const transform = (member) => {
|
|
1633
1525
|
if (!hasSiblingProperties) return this.transform(member);
|
|
1634
1526
|
unionsWithSiblings.add(member);
|
|
@@ -1642,7 +1534,18 @@ function buildZodNodes(dialect, cyclicSchemas) {
|
|
|
1642
1534
|
if (!first) return "";
|
|
1643
1535
|
const firstBase = transform(first);
|
|
1644
1536
|
if (!firstBase) return "";
|
|
1645
|
-
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
|
+
}
|
|
1646
1549
|
return rest.reduce((acc, member) => {
|
|
1647
1550
|
const constraint = getMemberConstraint({
|
|
1648
1551
|
member,
|
|
@@ -1657,15 +1560,57 @@ function buildZodNodes(dialect, cyclicSchemas) {
|
|
|
1657
1560
|
}
|
|
1658
1561
|
};
|
|
1659
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
|
+
}
|
|
1660
1601
|
// src/printers/printerZod.ts
|
|
1661
1602
|
function strictOneOfMember(member, node, cyclicSchemas) {
|
|
1662
|
-
if (node.type === _kubb_kit.ast.
|
|
1603
|
+
if (node.type === _kubb_kit.ast.schema.types.ref) {
|
|
1663
1604
|
if (member.startsWith("z.lazy(")) return member;
|
|
1664
|
-
const refName = _kubb_kit.ast.
|
|
1605
|
+
const refName = _kubb_kit.ast.ref.getName(node);
|
|
1665
1606
|
if (refName && cyclicSchemas?.has(refName)) return member;
|
|
1666
|
-
const schema =
|
|
1607
|
+
const schema = _kubb_kit.ast.ref.mergeSchema(node);
|
|
1667
1608
|
if (schema.nullable || schema.optional || node.nullable || node.optional) return member;
|
|
1668
|
-
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
|
+
}
|
|
1669
1614
|
}
|
|
1670
1615
|
return member;
|
|
1671
1616
|
}
|
|
@@ -1681,7 +1626,7 @@ function buildZodObjectShape(ctx, node) {
|
|
|
1681
1626
|
optional: schema.optional || required === false,
|
|
1682
1627
|
nullish: schema.nullish,
|
|
1683
1628
|
defaultValue: meta.default,
|
|
1684
|
-
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,
|
|
1685
1630
|
examples: meta.examples
|
|
1686
1631
|
});
|
|
1687
1632
|
if (!cyclic) return `${objectKey(name)}: ${body}`;
|
|
@@ -1702,21 +1647,22 @@ function buildZodObjectShape(ctx, node) {
|
|
|
1702
1647
|
function recursiveGetterType({ ctx, schema, required }) {
|
|
1703
1648
|
function baseType(schema) {
|
|
1704
1649
|
if (ctx.options.nodes?.[schema.type]) return void 0;
|
|
1705
|
-
if (schema.type === _kubb_kit.ast.
|
|
1650
|
+
if (schema.type === _kubb_kit.ast.schema.types.ref) {
|
|
1706
1651
|
const saved = ctx.options.cyclicSchemas;
|
|
1707
1652
|
ctx.options.cyclicSchemas = void 0;
|
|
1708
1653
|
const name = ctx.transform(schema);
|
|
1709
1654
|
ctx.options.cyclicSchemas = saved;
|
|
1710
1655
|
return name ? `typeof ${name}` : void 0;
|
|
1711
1656
|
}
|
|
1712
|
-
if (schema.type === _kubb_kit.ast.
|
|
1657
|
+
if (schema.type === _kubb_kit.ast.schema.types.array && schema.items?.length === 1) {
|
|
1713
1658
|
const item = baseType(schema.items[0]);
|
|
1714
|
-
|
|
1659
|
+
if (!item) return void 0;
|
|
1660
|
+
return `z.ZodArray<${schema.items[0].nullable ? `z.ZodNullable<${item}>` : item}>`;
|
|
1715
1661
|
}
|
|
1716
1662
|
}
|
|
1717
1663
|
const base = baseType(schema);
|
|
1718
1664
|
if (!base) return void 0;
|
|
1719
|
-
const meta =
|
|
1665
|
+
const meta = _kubb_kit.ast.ref.mergeSchema(schema);
|
|
1720
1666
|
const optional = schema.optional || required === false;
|
|
1721
1667
|
const wrapped = (() => {
|
|
1722
1668
|
if (schema.nullish || meta.nullable && optional) return `z.ZodOptional<z.ZodNullable<${base}>>`;
|
|
@@ -1731,12 +1677,12 @@ function recursiveGetterType({ ctx, schema, required }) {
|
|
|
1731
1677
|
* `direction` themselves, so {@link containsDirectionalNode} probes their children instead.
|
|
1732
1678
|
*/
|
|
1733
1679
|
const CONTAINER_TYPES = /* @__PURE__ */ new Set([
|
|
1734
|
-
_kubb_kit.ast.
|
|
1735
|
-
_kubb_kit.ast.
|
|
1736
|
-
_kubb_kit.ast.
|
|
1737
|
-
_kubb_kit.ast.
|
|
1738
|
-
_kubb_kit.ast.
|
|
1739
|
-
_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
|
|
1740
1686
|
]);
|
|
1741
1687
|
/**
|
|
1742
1688
|
* Runs the node's effective handler (a `printer.nodes` override, else the built-in) once per
|
|
@@ -1767,7 +1713,7 @@ function variesByDirection({ node, printerOptions }) {
|
|
|
1767
1713
|
*/
|
|
1768
1714
|
function containsDirectionalNode({ node, printerOptions }) {
|
|
1769
1715
|
if (!node) return false;
|
|
1770
|
-
return _kubb_kit.ast.
|
|
1716
|
+
return _kubb_kit.ast.schema.find(node, {
|
|
1771
1717
|
predicate: (schema) => variesByDirection({
|
|
1772
1718
|
node: schema,
|
|
1773
1719
|
printerOptions
|
|
@@ -1779,10 +1725,10 @@ function containsDirectionalNode({ node, printerOptions }) {
|
|
|
1779
1725
|
* Names of the `$ref` schemas the generator should route to their input (encode) variant.
|
|
1780
1726
|
*/
|
|
1781
1727
|
function collectDirectionalRefNames({ node, printerOptions }) {
|
|
1782
|
-
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({
|
|
1783
1729
|
node: n,
|
|
1784
1730
|
printerOptions
|
|
1785
|
-
}) ? _kubb_kit.ast.
|
|
1731
|
+
}) ? _kubb_kit.ast.ref.getName(n) ?? void 0 : void 0 });
|
|
1786
1732
|
}
|
|
1787
1733
|
/**
|
|
1788
1734
|
* Handlers that never recurse into children, so {@link variesByDirection} can call one directly
|
|
@@ -1804,6 +1750,7 @@ const standardDialect = {
|
|
|
1804
1750
|
coercedNumber: (schema) => `z.coerce.number().pipe(${schema})`,
|
|
1805
1751
|
catchall: (object, value) => `${object}.catchall(${value})`,
|
|
1806
1752
|
nullable: (value) => `${value}.nullable()`,
|
|
1753
|
+
refine: (value, check) => `${value}.refine(${check})`,
|
|
1807
1754
|
unique: (array) => `${array}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`,
|
|
1808
1755
|
extend: (base, shape) => `${base}.extend(${shape})`,
|
|
1809
1756
|
intersection: (left, right) => `${left}.and(${right})`,
|
|
@@ -1815,8 +1762,8 @@ const standardDialect = {
|
|
|
1815
1762
|
};
|
|
1816
1763
|
const scalarNodes = {
|
|
1817
1764
|
...buildScalarNodes(standardDialect),
|
|
1818
|
-
bigint() {
|
|
1819
|
-
return
|
|
1765
|
+
bigint(node) {
|
|
1766
|
+
return `z.coerce.bigint()${numberConstraints(bigintConstraints(node), "n")}`;
|
|
1820
1767
|
},
|
|
1821
1768
|
date(node) {
|
|
1822
1769
|
if (node.representation !== "date") return "z.iso.date()";
|
|
@@ -1849,14 +1796,14 @@ const scalarNodes = {
|
|
|
1849
1796
|
* const code = printer.print(stringNode) // "z.string()"
|
|
1850
1797
|
* ```
|
|
1851
1798
|
*/
|
|
1852
|
-
const printerZod = _kubb_kit.
|
|
1799
|
+
const printerZod = (0, _kubb_kit.createPrinter)((options) => {
|
|
1853
1800
|
return {
|
|
1854
1801
|
name: "zod",
|
|
1855
1802
|
options,
|
|
1856
|
-
nodes: {
|
|
1803
|
+
nodes: withInputChecks({
|
|
1857
1804
|
...buildZodNodes(standardDialect, options.cyclicSchemas),
|
|
1858
1805
|
...scalarNodes
|
|
1859
|
-
},
|
|
1806
|
+
}),
|
|
1860
1807
|
overrides: options.nodes,
|
|
1861
1808
|
print(node) {
|
|
1862
1809
|
return printWithOmit(this, node, (value, meta) => applyModifiers({
|
|
@@ -1913,18 +1860,19 @@ const miniDialect = {
|
|
|
1913
1860
|
coercedNumber: (schema) => `z.pipe(z.coerce.number(), ${schema})`,
|
|
1914
1861
|
catchall: (object, value) => `z.catchall(${object}, ${value})`,
|
|
1915
1862
|
nullable: (value) => `z.nullable(${value})`,
|
|
1863
|
+
refine: (value, check) => `${value}.check(z.refine(${check}))`,
|
|
1916
1864
|
unique: (array) => `${array}.check(z.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" }))`,
|
|
1917
1865
|
extend: (base, shape) => `z.extend(${base}, ${shape})`,
|
|
1918
1866
|
intersection: (left, right) => `z.intersection(${left}, ${right})`
|
|
1919
1867
|
};
|
|
1920
|
-
const printerZodMini = _kubb_kit.
|
|
1868
|
+
const printerZodMini = (0, _kubb_kit.createPrinter)((options) => {
|
|
1921
1869
|
return {
|
|
1922
1870
|
name: "zod-mini",
|
|
1923
1871
|
options,
|
|
1924
|
-
nodes: {
|
|
1872
|
+
nodes: withInputChecks({
|
|
1925
1873
|
...buildZodNodes(miniDialect, options.cyclicSchemas),
|
|
1926
1874
|
bigint(node) {
|
|
1927
|
-
return `z.coerce.bigint()${numberChecksMini(node)}`;
|
|
1875
|
+
return `z.coerce.bigint()${numberChecksMini(bigintConstraints(node), "n")}`;
|
|
1928
1876
|
},
|
|
1929
1877
|
date(node) {
|
|
1930
1878
|
if (node.representation === "string") return "z.iso.date()";
|
|
@@ -1937,7 +1885,7 @@ const printerZodMini = _kubb_kit.ast.createPrinter((options) => {
|
|
|
1937
1885
|
if (node.representation === "string") return "z.iso.time()";
|
|
1938
1886
|
return "z.date()";
|
|
1939
1887
|
}
|
|
1940
|
-
},
|
|
1888
|
+
}),
|
|
1941
1889
|
overrides: options.nodes,
|
|
1942
1890
|
print(node) {
|
|
1943
1891
|
return printWithOmit(this, node, (value, meta) => applyMiniModifiers({
|
|
@@ -2024,7 +1972,8 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2024
1972
|
node,
|
|
2025
1973
|
root,
|
|
2026
1974
|
output,
|
|
2027
|
-
group: group ?? void 0
|
|
1975
|
+
group: group ?? void 0,
|
|
1976
|
+
checks: true
|
|
2028
1977
|
});
|
|
2029
1978
|
const inputImportEntries = hasDirectionalNode ? [...directionalRefNames].map((schemaName) => ({
|
|
2030
1979
|
name: [resolver.schema.inputName(schemaName)],
|
|
@@ -2115,7 +2064,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2115
2064
|
});
|
|
2116
2065
|
},
|
|
2117
2066
|
operation(node, ctx) {
|
|
2118
|
-
if (!_kubb_kit.ast.
|
|
2067
|
+
if (!_kubb_kit.ast.http.isOperation(node)) return null;
|
|
2119
2068
|
const { adapter, config, resolver, root } = ctx;
|
|
2120
2069
|
const { output, coercion, guidType, regexType, oneOfType, compile, mini, inferred, importPath, group, printer } = ctx.options;
|
|
2121
2070
|
const dateType = getOasAdapter(adapter).options.dateType;
|
|
@@ -2144,17 +2093,18 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2144
2093
|
node: schema,
|
|
2145
2094
|
printerOptions
|
|
2146
2095
|
})) : null;
|
|
2096
|
+
const schemaPrinter = getPrinter(resolver, {
|
|
2097
|
+
...printerOptions,
|
|
2098
|
+
keysToOmit
|
|
2099
|
+
}, mini, direction);
|
|
2147
2100
|
const imports = resolver.imports({
|
|
2148
2101
|
node: schema,
|
|
2149
2102
|
root,
|
|
2150
2103
|
output,
|
|
2151
2104
|
group: group ?? void 0,
|
|
2105
|
+
checks: true,
|
|
2152
2106
|
name: (schemaName) => directionalRefNames?.has(schemaName) ? resolver.schema.inputName(schemaName) : resolver.name(schemaName)
|
|
2153
2107
|
});
|
|
2154
|
-
const schemaPrinter = getPrinter(resolver, {
|
|
2155
|
-
...printerOptions,
|
|
2156
|
-
keysToOmit
|
|
2157
|
-
}, mini, direction);
|
|
2158
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, {
|
|
2159
2109
|
root: meta.file.path,
|
|
2160
2110
|
path: imp.path,
|
|
@@ -2194,7 +2144,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2194
2144
|
})] });
|
|
2195
2145
|
}
|
|
2196
2146
|
function buildResponseUnion({ responses, name, fallbackUnknown }) {
|
|
2197
|
-
if (new Set(
|
|
2147
|
+
if (new Set(_kubb_kit.ast.http.responses.getImportedNames(responses).map((refName) => resolver.name(refName))).has(name)) return null;
|
|
2198
2148
|
const members = responses.map((res) => _kubb_kit.ast.factory.createSchema({
|
|
2199
2149
|
type: "ref",
|
|
2200
2150
|
name: resolver.response.status(node, res.statusCode)
|
|
@@ -2227,14 +2177,27 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2227
2177
|
keysToOmit: primary?.keysToOmit
|
|
2228
2178
|
});
|
|
2229
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);
|
|
2230
2193
|
const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
|
|
2231
|
-
const successResponsesWithSchema =
|
|
2194
|
+
const successResponsesWithSchema = responsesWithSchema.filter((res) => _kubb_kit.ast.http.status.isSuccess(res));
|
|
2232
2195
|
const responseUnionSchema = responsesWithSchema.length > 0 ? buildResponseUnion({
|
|
2233
2196
|
responses: successResponsesWithSchema,
|
|
2234
2197
|
name: resolver.response.response(node),
|
|
2235
2198
|
fallbackUnknown: true
|
|
2236
2199
|
}) : null;
|
|
2237
|
-
const errorResponsesWithSchema = responsesWithSchema.filter((res) => !
|
|
2200
|
+
const errorResponsesWithSchema = responsesWithSchema.filter((res) => !_kubb_kit.ast.http.status.isSuccess(res));
|
|
2238
2201
|
const errorUnionSchema = errorResponsesWithSchema.length > 0 ? buildResponseUnion({
|
|
2239
2202
|
responses: errorResponsesWithSchema,
|
|
2240
2203
|
name: resolver.response.error(node),
|
|
@@ -2261,7 +2224,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2261
2224
|
description: node.requestBody.description ?? schema.description
|
|
2262
2225
|
}), "encode");
|
|
2263
2226
|
})();
|
|
2264
|
-
const { path, query, header, cookie } =
|
|
2227
|
+
const { path, query, header, cookie } = _kubb_kit.ast.http.groupParameters(node);
|
|
2265
2228
|
const paramGroupSchemas = [
|
|
2266
2229
|
{
|
|
2267
2230
|
kind: "path",
|
|
@@ -2280,15 +2243,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2280
2243
|
params: cookie
|
|
2281
2244
|
}
|
|
2282
2245
|
].filter(({ params }) => params.length > 0).map(({ kind, params }) => renderSchemaEntry({
|
|
2283
|
-
schema:
|
|
2284
|
-
type: "object",
|
|
2285
|
-
primitive: "object",
|
|
2286
|
-
properties: params.map((param) => _kubb_kit.ast.factory.createProperty({
|
|
2287
|
-
name: param.name,
|
|
2288
|
-
required: param.required,
|
|
2289
|
-
schema: param.schema
|
|
2290
|
-
}))
|
|
2291
|
-
}),
|
|
2246
|
+
schema: toParamsSchema(params),
|
|
2292
2247
|
name: resolver.param[kind](node, params[0]),
|
|
2293
2248
|
direction: "encode"
|
|
2294
2249
|
}));
|
|
@@ -2329,6 +2284,7 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2329
2284
|
}),
|
|
2330
2285
|
paramSchemas,
|
|
2331
2286
|
responseSchemas,
|
|
2287
|
+
responseHeaderSchemas,
|
|
2332
2288
|
responseUnionSchema,
|
|
2333
2289
|
errorUnionSchema,
|
|
2334
2290
|
requestSchema,
|
|
@@ -2341,11 +2297,11 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
|
|
|
2341
2297
|
});
|
|
2342
2298
|
// kubb-extension.json
|
|
2343
2299
|
var compatibility = {
|
|
2344
|
-
"kubb": ">=5.
|
|
2300
|
+
"kubb": ">=5.18.4",
|
|
2345
2301
|
"node": ">=22.22"
|
|
2346
2302
|
};
|
|
2347
2303
|
// package.json
|
|
2348
|
-
var version = "5.9.0
|
|
2304
|
+
var version = "5.9.0";
|
|
2349
2305
|
// src/resolvers/resolverZod.ts
|
|
2350
2306
|
/**
|
|
2351
2307
|
* Default resolver used by `@kubb/plugin-zod`. Decides the names and file
|
|
@@ -2365,15 +2321,15 @@ var version = "5.9.0-canary.20261111T115959";
|
|
|
2365
2321
|
const resolverZod = (0, _kubb_kit.createResolver)({
|
|
2366
2322
|
pluginName: "plugin-zod",
|
|
2367
2323
|
name(name) {
|
|
2368
|
-
return this.memo(`name:${name}`, () => ensureValidVarName(
|
|
2324
|
+
return this.memo(`name:${name}`, () => ensureValidVarName(_kubb_kit.casing.camel(name, { suffix: "schema" })));
|
|
2369
2325
|
},
|
|
2370
|
-
file: createCasedFile((part) =>
|
|
2326
|
+
file: createCasedFile((part) => _kubb_kit.casing.camel(part, { suffix: "schema" })),
|
|
2371
2327
|
schema: {
|
|
2372
2328
|
typeName(name) {
|
|
2373
|
-
return this.memo(`typeName:${name}`, () => ensureValidVarName(
|
|
2329
|
+
return this.memo(`typeName:${name}`, () => ensureValidVarName(_kubb_kit.casing.pascal(name, { suffix: "schema type" })));
|
|
2374
2330
|
},
|
|
2375
2331
|
type(name) {
|
|
2376
|
-
return this.memo(`type:${name}`, () => ensureValidVarName(
|
|
2332
|
+
return this.memo(`type:${name}`, () => ensureValidVarName(_kubb_kit.casing.pascal(name, { suffix: "type" })));
|
|
2377
2333
|
},
|
|
2378
2334
|
inputName(name) {
|
|
2379
2335
|
return this.name(`${name} input`);
|
|
@@ -2382,10 +2338,10 @@ const resolverZod = (0, _kubb_kit.createResolver)({
|
|
|
2382
2338
|
return this.schema.typeName(`${name} input`);
|
|
2383
2339
|
},
|
|
2384
2340
|
isName(name) {
|
|
2385
|
-
return ensureValidVarName(
|
|
2341
|
+
return ensureValidVarName(_kubb_kit.casing.camel(name, { prefix: "is" }));
|
|
2386
2342
|
},
|
|
2387
2343
|
assertName(name) {
|
|
2388
|
-
return ensureValidVarName(
|
|
2344
|
+
return ensureValidVarName(_kubb_kit.casing.camel(name, { prefix: "assert" }));
|
|
2389
2345
|
}
|
|
2390
2346
|
},
|
|
2391
2347
|
param: createOperationParamResolver(),
|
|
@@ -2459,7 +2415,7 @@ const pluginZod = (0, _kubb_kit.defineExtension)({
|
|
|
2459
2415
|
exclude,
|
|
2460
2416
|
include,
|
|
2461
2417
|
override,
|
|
2462
|
-
group:
|
|
2418
|
+
group: group ?? null,
|
|
2463
2419
|
importPath,
|
|
2464
2420
|
coercion,
|
|
2465
2421
|
inferred: inferred === "direction" ? "codec" : inferred,
|