@kubb/plugin-zod 5.0.0-alpha.9 → 5.0.0-beta.15

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.
Files changed (46) hide show
  1. package/LICENSE +17 -10
  2. package/README.md +25 -7
  3. package/dist/index.cjs +1129 -105
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.d.ts +369 -4
  6. package/dist/index.js +1116 -105
  7. package/dist/index.js.map +1 -1
  8. package/extension.yaml +505 -0
  9. package/package.json +43 -73
  10. package/src/components/Operations.tsx +25 -18
  11. package/src/components/Zod.tsx +21 -121
  12. package/src/constants.ts +5 -0
  13. package/src/generators/zodGenerator.tsx +213 -166
  14. package/src/index.ts +11 -2
  15. package/src/plugin.ts +67 -156
  16. package/src/printers/printerZod.ts +368 -0
  17. package/src/printers/printerZodMini.ts +313 -0
  18. package/src/resolvers/resolverZod.ts +57 -0
  19. package/src/types.ts +130 -115
  20. package/src/utils.ts +222 -0
  21. package/dist/components-B7zUFnAm.cjs +0 -890
  22. package/dist/components-B7zUFnAm.cjs.map +0 -1
  23. package/dist/components-eECfXVou.js +0 -842
  24. package/dist/components-eECfXVou.js.map +0 -1
  25. package/dist/components.cjs +0 -4
  26. package/dist/components.d.ts +0 -56
  27. package/dist/components.js +0 -2
  28. package/dist/generators-BjPDdJUz.cjs +0 -301
  29. package/dist/generators-BjPDdJUz.cjs.map +0 -1
  30. package/dist/generators-lTWPS6oN.js +0 -290
  31. package/dist/generators-lTWPS6oN.js.map +0 -1
  32. package/dist/generators.cjs +0 -4
  33. package/dist/generators.d.ts +0 -508
  34. package/dist/generators.js +0 -2
  35. package/dist/templates/ToZod.source.cjs +0 -7
  36. package/dist/templates/ToZod.source.cjs.map +0 -1
  37. package/dist/templates/ToZod.source.d.ts +0 -7
  38. package/dist/templates/ToZod.source.js +0 -6
  39. package/dist/templates/ToZod.source.js.map +0 -1
  40. package/dist/types-CoCoOc2u.d.ts +0 -172
  41. package/src/components/index.ts +0 -2
  42. package/src/generators/index.ts +0 -2
  43. package/src/generators/operationsGenerator.tsx +0 -50
  44. package/src/parser.ts +0 -909
  45. package/src/templates/ToZod.source.ts +0 -4
  46. package/templates/ToZod.ts +0 -61
package/dist/index.js CHANGED
@@ -1,10 +1,7 @@
1
- import "./chunk--u3MIqq1.js";
2
- import { n as operationsGenerator, t as zodGenerator } from "./generators-lTWPS6oN.js";
3
- import { source } from "./templates/ToZod.source.js";
4
- import path from "node:path";
5
- import { createPlugin, getBarrelFiles, getMode, satisfiesDependency } from "@kubb/core";
6
- import { OperationGenerator, SchemaGenerator, pluginOasName } from "@kubb/plugin-oas";
7
- import { pluginTsName } from "@kubb/plugin-ts";
1
+ import { t as __name } from "./chunk--u3MIqq1.js";
2
+ import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
3
+ import { Const, File, Type, jsxRendererSync } from "@kubb/renderer-jsx";
4
+ import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
8
5
  //#region ../../internals/utils/src/casing.ts
9
6
  /**
10
7
  * Shared implementation for camelCase and PascalCase conversion.
@@ -24,9 +21,12 @@ function toCamelOrPascal(text, pascal) {
24
21
  * Splits `text` on `.` and applies `transformPart` to each segment.
25
22
  * The last segment receives `isLast = true`, all earlier segments receive `false`.
26
23
  * Segments are joined with `/` to form a file path.
24
+ *
25
+ * Only splits on dots followed by a letter so that version numbers
26
+ * embedded in operationIds (e.g. `v2025.0`) are kept intact.
27
27
  */
28
28
  function applyToFileParts(text, transformPart) {
29
- const parts = text.split(".");
29
+ const parts = text.split(/\.(?=[a-zA-Z])/);
30
30
  return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
31
31
  }
32
32
  /**
@@ -60,116 +60,1127 @@ function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
60
60
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
61
61
  }
62
62
  //#endregion
63
+ //#region ../../internals/utils/src/string.ts
64
+ /**
65
+ * Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
66
+ * Returns the string unchanged when no balanced quote pair is found.
67
+ *
68
+ * @example
69
+ * trimQuotes('"hello"') // 'hello'
70
+ * trimQuotes('hello') // 'hello'
71
+ */
72
+ function trimQuotes(text) {
73
+ if (text.length >= 2) {
74
+ const first = text[0];
75
+ const last = text[text.length - 1];
76
+ if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
77
+ }
78
+ return text;
79
+ }
80
+ //#endregion
81
+ //#region ../../internals/utils/src/object.ts
82
+ /**
83
+ * Serializes a primitive value to a JSON string literal, stripping any surrounding quote characters first.
84
+ *
85
+ * @example
86
+ * stringify('hello') // '"hello"'
87
+ * stringify('"hello"') // '"hello"'
88
+ */
89
+ function stringify(value) {
90
+ if (value === void 0 || value === null) return "\"\"";
91
+ return JSON.stringify(trimQuotes(value.toString()));
92
+ }
93
+ /**
94
+ * Converts a plain object into a multiline key-value string suitable for embedding in generated code.
95
+ * Nested objects are recursively stringified with indentation.
96
+ *
97
+ * @example
98
+ * stringifyObject({ foo: 'bar', nested: { a: 1 } })
99
+ * // 'foo: bar,\nnested: {\n a: 1\n }'
100
+ */
101
+ function stringifyObject(value) {
102
+ return Object.entries(value).map(([key, val]) => {
103
+ if (val !== null && typeof val === "object") return `${key}: {\n ${stringifyObject(val)}\n }`;
104
+ return `${key}: ${val}`;
105
+ }).filter(Boolean).join(",\n");
106
+ }
107
+ //#endregion
108
+ //#region ../../internals/utils/src/regexp.ts
109
+ /**
110
+ * Converts a pattern string into a `new RegExp(...)` constructor call or a regex literal string.
111
+ * Inline flags expressed as `^(?im)` prefixes are extracted and applied to the resulting expression.
112
+ * Pass `null` as the second argument to emit a `/pattern/flags` literal instead.
113
+ *
114
+ * @example
115
+ * toRegExpString('^(?im)foo') // → 'new RegExp("foo", "im")'
116
+ * toRegExpString('^(?im)foo', null) // → '/foo/im'
117
+ */
118
+ function toRegExpString(text, func = "RegExp") {
119
+ const raw = trimQuotes(text);
120
+ const match = raw.match(/^\^(\(\?([igmsuy]+)\))/i);
121
+ const replacementTarget = match?.[1] ?? "";
122
+ const matchedFlags = match?.[2];
123
+ const cleaned = raw.replace(/^\\?\//, "").replace(/\\?\/$/, "").replace(replacementTarget, "");
124
+ const { source, flags } = new RegExp(cleaned, matchedFlags);
125
+ if (func === null) return `/${source}/${flags}`;
126
+ return `new ${func}(${JSON.stringify(source)}${flags ? `, ${JSON.stringify(flags)}` : ""})`;
127
+ }
128
+ //#endregion
129
+ //#region src/components/Operations.tsx
130
+ function Operations({ name, operations }) {
131
+ const operationsJSON = operations.reduce((prev, acc) => {
132
+ prev[`"${acc.node.operationId}"`] = acc.data;
133
+ return prev;
134
+ }, {});
135
+ const pathsJSON = operations.reduce((prev, acc) => {
136
+ prev[`"${acc.node.path}"`] = {
137
+ ...prev[`"${acc.node.path}"`] ?? {},
138
+ [acc.node.method]: `operations["${acc.node.operationId}"]`
139
+ };
140
+ return prev;
141
+ }, {});
142
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
143
+ /* @__PURE__ */ jsx(File.Source, {
144
+ name: "OperationSchema",
145
+ isExportable: true,
146
+ isIndexable: true,
147
+ children: /* @__PURE__ */ jsx(Type, {
148
+ name: "OperationSchema",
149
+ export: true,
150
+ children: `{
151
+ readonly request: z.ZodTypeAny | undefined;
152
+ readonly parameters: {
153
+ readonly path: z.ZodTypeAny | undefined;
154
+ readonly query: z.ZodTypeAny | undefined;
155
+ readonly header: z.ZodTypeAny | undefined;
156
+ };
157
+ readonly responses: {
158
+ readonly [status: number]: z.ZodTypeAny;
159
+ readonly default: z.ZodTypeAny;
160
+ };
161
+ readonly errors: {
162
+ readonly [status: number]: z.ZodTypeAny;
163
+ };
164
+ }`
165
+ })
166
+ }),
167
+ /* @__PURE__ */ jsx(File.Source, {
168
+ name: "OperationsMap",
169
+ isExportable: true,
170
+ isIndexable: true,
171
+ children: /* @__PURE__ */ jsx(Type, {
172
+ name: "OperationsMap",
173
+ export: true,
174
+ children: "Record<string, OperationSchema>"
175
+ })
176
+ }),
177
+ /* @__PURE__ */ jsx(File.Source, {
178
+ name,
179
+ isExportable: true,
180
+ isIndexable: true,
181
+ children: /* @__PURE__ */ jsx(Const, {
182
+ export: true,
183
+ name,
184
+ asConst: true,
185
+ children: `{${stringifyObject(operationsJSON)}}`
186
+ })
187
+ }),
188
+ /* @__PURE__ */ jsx(File.Source, {
189
+ name: "paths",
190
+ isExportable: true,
191
+ isIndexable: true,
192
+ children: /* @__PURE__ */ jsx(Const, {
193
+ export: true,
194
+ name: "paths",
195
+ asConst: true,
196
+ children: `{${stringifyObject(pathsJSON)}}`
197
+ })
198
+ })
199
+ ] });
200
+ }
201
+ //#endregion
202
+ //#region src/components/Zod.tsx
203
+ function Zod({ name, node, printer, inferTypeName }) {
204
+ const output = printer.print(node);
205
+ if (!output) return;
206
+ return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(File.Source, {
207
+ name,
208
+ isExportable: true,
209
+ isIndexable: true,
210
+ children: /* @__PURE__ */ jsx(Const, {
211
+ export: true,
212
+ name,
213
+ children: output
214
+ })
215
+ }), inferTypeName && /* @__PURE__ */ jsx(File.Source, {
216
+ name: inferTypeName,
217
+ isExportable: true,
218
+ isIndexable: true,
219
+ isTypeOnly: true,
220
+ children: /* @__PURE__ */ jsx(Type, {
221
+ export: true,
222
+ name: inferTypeName,
223
+ children: `z.infer<typeof ${name}>`
224
+ })
225
+ })] });
226
+ }
227
+ //#endregion
228
+ //#region src/constants.ts
229
+ /**
230
+ * Import paths that use a namespace import (`import * as z from '...'`).
231
+ * All other import paths use a named import (`import { z } from '...'`).
232
+ */
233
+ const ZOD_NAMESPACE_IMPORTS = new Set(["zod", "zod/mini"]);
234
+ //#endregion
235
+ //#region src/utils.ts
236
+ /**
237
+ * Returns `true` when the given coercion option enables coercion for the specified type.
238
+ */
239
+ function shouldCoerce(coercion, type) {
240
+ if (coercion === void 0 || coercion === false) return false;
241
+ if (coercion === true) return true;
242
+ return !!coercion[type];
243
+ }
244
+ /**
245
+ * Collects all resolved schema names for an operation's parameters and responses
246
+ * into a single lookup object, useful for building imports and type references.
247
+ */
248
+ function buildSchemaNames(node, { params, resolver }) {
249
+ const pathParam = params.find((p) => p.in === "path");
250
+ const queryParam = params.find((p) => p.in === "query");
251
+ const headerParam = params.find((p) => p.in === "header");
252
+ const responses = {};
253
+ const errors = {};
254
+ for (const res of node.responses) {
255
+ const name = resolver.resolveResponseStatusName(node, res.statusCode);
256
+ const statusNum = Number(res.statusCode);
257
+ if (!Number.isNaN(statusNum)) {
258
+ responses[statusNum] = name;
259
+ if (statusNum >= 400) errors[statusNum] = name;
260
+ }
261
+ }
262
+ responses["default"] = resolver.resolveResponseName(node);
263
+ return {
264
+ request: node.requestBody?.content?.[0]?.schema ? resolver.resolveDataName(node) : void 0,
265
+ parameters: {
266
+ path: pathParam ? resolver.resolvePathParamsName(node, pathParam) : void 0,
267
+ query: queryParam ? resolver.resolveQueryParamsName(node, queryParam) : void 0,
268
+ header: headerParam ? resolver.resolveHeaderParamsName(node, headerParam) : void 0
269
+ },
270
+ responses,
271
+ errors
272
+ };
273
+ }
274
+ /**
275
+ * Format a default value as a code-level literal.
276
+ * Objects become `{}`, primitives become their string representation, strings are quoted.
277
+ */
278
+ function formatDefault(value) {
279
+ if (typeof value === "string") return stringify(value);
280
+ if (typeof value === "object" && value !== null) return "{}";
281
+ return String(value ?? "");
282
+ }
283
+ /**
284
+ * Format a primitive enum/literal value.
285
+ * Strings are quoted; numbers and booleans are emitted raw.
286
+ */
287
+ function formatLiteral(v) {
288
+ if (typeof v === "string") return stringify(v);
289
+ return String(v);
290
+ }
291
+ /**
292
+ * Build `.min()` / `.max()` / `.gt()` / `.lt()` constraint chains for numbers
293
+ * using the standard chainable Zod v4 API.
294
+ */
295
+ function numberConstraints({ min, max, exclusiveMinimum, exclusiveMaximum, multipleOf }) {
296
+ return [
297
+ min !== void 0 ? `.min(${min})` : "",
298
+ max !== void 0 ? `.max(${max})` : "",
299
+ exclusiveMinimum !== void 0 ? `.gt(${exclusiveMinimum})` : "",
300
+ exclusiveMaximum !== void 0 ? `.lt(${exclusiveMaximum})` : "",
301
+ multipleOf !== void 0 ? `.multipleOf(${multipleOf})` : ""
302
+ ].join("");
303
+ }
304
+ /**
305
+ * Build `.min()` / `.max()` / `.regex()` chains for strings/arrays
306
+ * using the standard chainable Zod v4 API.
307
+ */
308
+ function lengthConstraints({ min, max, pattern }) {
309
+ return [
310
+ min !== void 0 ? `.min(${min})` : "",
311
+ max !== void 0 ? `.max(${max})` : "",
312
+ pattern !== void 0 ? `.regex(${toRegExpString(pattern, null)})` : ""
313
+ ].join("");
314
+ }
315
+ /**
316
+ * Build `.check(z.minimum(), z.maximum())` for `zod/mini` numeric constraints.
317
+ */
318
+ function numberChecksMini({ min, max, exclusiveMinimum, exclusiveMaximum, multipleOf }) {
319
+ const checks = [];
320
+ if (min !== void 0) checks.push(`z.minimum(${min})`);
321
+ if (max !== void 0) checks.push(`z.maximum(${max})`);
322
+ if (exclusiveMinimum !== void 0) checks.push(`z.minimum(${exclusiveMinimum}, { exclusive: true })`);
323
+ if (exclusiveMaximum !== void 0) checks.push(`z.maximum(${exclusiveMaximum}, { exclusive: true })`);
324
+ if (multipleOf !== void 0) checks.push(`z.multipleOf(${multipleOf})`);
325
+ return checks.length ? `.check(${checks.join(", ")})` : "";
326
+ }
327
+ /**
328
+ * Build `.check(z.minLength(), z.maxLength(), z.regex())` for `zod/mini` length constraints.
329
+ */
330
+ function lengthChecksMini({ min, max, pattern }) {
331
+ const checks = [];
332
+ if (min !== void 0) checks.push(`z.minLength(${min})`);
333
+ if (max !== void 0) checks.push(`z.maxLength(${max})`);
334
+ if (pattern !== void 0) checks.push(`z.regex(${toRegExpString(pattern, null)})`);
335
+ return checks.length ? `.check(${checks.join(", ")})` : "";
336
+ }
337
+ /**
338
+ * Apply nullable / optional / nullish modifiers and an optional `.describe()` call
339
+ * to a schema value string using the chainable Zod v4 API.
340
+ */
341
+ function applyModifiers({ value, nullable, optional, nullish, defaultValue, description }) {
342
+ let result = value;
343
+ if (nullish || nullable && optional) result = `${result}.nullish()`;
344
+ else if (optional) result = `${result}.optional()`;
345
+ else if (nullable) result = `${result}.nullable()`;
346
+ if (defaultValue !== void 0) result = `${result}.default(${formatDefault(defaultValue)})`;
347
+ if (description) result = `${result}.describe(${stringify(description)})`;
348
+ return result;
349
+ }
350
+ /**
351
+ * Apply nullable / optional / nullish modifiers using the functional `zod/mini` API
352
+ * (`z.nullable()`, `z.optional()`, `z.nullish()`).
353
+ */
354
+ function applyMiniModifiers({ value, nullable, optional, nullish, defaultValue }) {
355
+ let result = value;
356
+ if (nullish) result = `z.nullish(${result})`;
357
+ else {
358
+ if (nullable) result = `z.nullable(${result})`;
359
+ if (optional) result = `z.optional(${result})`;
360
+ }
361
+ if (defaultValue !== void 0) result = `z._default(${result}, ${formatDefault(defaultValue)})`;
362
+ return result;
363
+ }
364
+ //#endregion
365
+ //#region src/printers/printerZod.ts
366
+ function strictOneOfMember$1(member, node) {
367
+ if (node.type === "object" && node.additionalProperties === void 0) return `${member}.strict()`;
368
+ if (node.type === "ref") {
369
+ if (member.startsWith("z.lazy(")) return member;
370
+ const schema = ast.syncSchemaRef(node);
371
+ if (schema.type === "object" && (schema.additionalProperties === void 0 || schema.additionalProperties === false)) return `${member}.strict()`;
372
+ }
373
+ return member;
374
+ }
375
+ __name(strictOneOfMember$1, "strictOneOfMember");
376
+ /**
377
+ * Zod v4 printer built with `definePrinter`.
378
+ *
379
+ * Converts a `SchemaNode` AST into a Zod v4 code string using the chainable API
380
+ * (`.optional()`, `.nullable()`, `.omit()`, etc.). For improved tree-shaking, see {@link printerZodMini}.
381
+ *
382
+ * @example Chainable API
383
+ * ```ts
384
+ * const printer = printerZod({ coercion: false })
385
+ * const code = printer.print(stringNode) // "z.string()"
386
+ * ```
387
+ */
388
+ const printerZod = ast.definePrinter((options) => {
389
+ return {
390
+ name: "zod",
391
+ options,
392
+ nodes: {
393
+ any: () => "z.any()",
394
+ unknown: () => "z.unknown()",
395
+ void: () => "z.void()",
396
+ never: () => "z.never()",
397
+ boolean: () => "z.boolean()",
398
+ null: () => "z.null()",
399
+ string(node) {
400
+ return `${shouldCoerce(this.options.coercion, "strings") ? "z.coerce.string()" : "z.string()"}${lengthConstraints(node)}`;
401
+ },
402
+ number(node) {
403
+ return `${shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.number()" : "z.number()"}${numberConstraints(node)}`;
404
+ },
405
+ integer(node) {
406
+ return `${shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.number().int()" : "z.int()"}${numberConstraints(node)}`;
407
+ },
408
+ bigint() {
409
+ return shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.bigint()" : "z.bigint()";
410
+ },
411
+ date(node) {
412
+ if (node.representation === "string") return "z.iso.date()";
413
+ return shouldCoerce(this.options.coercion, "dates") ? "z.coerce.date()" : "z.date()";
414
+ },
415
+ datetime(node) {
416
+ const offset = node.offset || this.options.dateType === "stringOffset";
417
+ const local = node.local || this.options.dateType === "stringLocal";
418
+ if (offset) return "z.iso.datetime({ offset: true })";
419
+ if (local) return "z.iso.datetime({ local: true })";
420
+ return "z.iso.datetime()";
421
+ },
422
+ time(node) {
423
+ if (node.representation === "string") return "z.iso.time()";
424
+ return shouldCoerce(this.options.coercion, "dates") ? "z.coerce.date()" : "z.date()";
425
+ },
426
+ uuid(node) {
427
+ return `${this.options.guidType === "guid" ? "z.guid()" : "z.uuid()"}${lengthConstraints(node)}`;
428
+ },
429
+ email(node) {
430
+ return `z.email()${lengthConstraints(node)}`;
431
+ },
432
+ url(node) {
433
+ return `z.url()${lengthConstraints(node)}`;
434
+ },
435
+ ipv4: () => "z.ipv4()",
436
+ ipv6: () => "z.ipv6()",
437
+ blob: () => "z.instanceof(File)",
438
+ enum(node) {
439
+ const nonNullValues = (node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []).filter((v) => v !== null);
440
+ if (node.namedEnumValues?.length) {
441
+ const literals = nonNullValues.map((v) => `z.literal(${formatLiteral(v)})`);
442
+ if (literals.length === 1) return literals[0];
443
+ return `z.union([${literals.join(", ")}])`;
444
+ }
445
+ return `z.enum([${nonNullValues.map(formatLiteral).join(", ")}])`;
446
+ },
447
+ ref(node) {
448
+ if (!node.name) return void 0;
449
+ const refName = node.ref ? ast.extractRefName(node.ref) ?? node.name : node.name;
450
+ const resolvedName = node.ref ? this.options.resolver?.default(refName, "function") ?? refName : node.name;
451
+ if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
452
+ return resolvedName;
453
+ },
454
+ object(node) {
455
+ let result = `z.object({\n ${node.properties.map((prop) => {
456
+ const { name: propName, schema } = prop;
457
+ const meta = ast.syncSchemaRef(schema);
458
+ const isNullable = meta.nullable;
459
+ const isOptional = schema.optional;
460
+ const isNullish = schema.nullish;
461
+ const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
462
+ const savedCyclicSchemas = this.options.cyclicSchemas;
463
+ if (hasSelfRef) this.options.cyclicSchemas = void 0;
464
+ const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: "unknown" }));
465
+ if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas;
466
+ const wrappedOutput = this.options.wrapOutput ? this.options.wrapOutput({
467
+ output: baseOutput,
468
+ schema
469
+ }) || baseOutput : baseOutput;
470
+ let descriptionToApply = meta.description;
471
+ if (schema.type !== "ref" && meta.type === "ref") descriptionToApply = void 0;
472
+ const value = applyModifiers({
473
+ value: wrappedOutput,
474
+ nullable: isNullable,
475
+ optional: isOptional,
476
+ nullish: isNullish,
477
+ defaultValue: meta.default,
478
+ description: descriptionToApply
479
+ });
480
+ if (hasSelfRef) return `get "${propName}"() { return ${value} }`;
481
+ return `"${propName}": ${value}`;
482
+ }).join(",\n ")}\n })`;
483
+ if (node.additionalProperties && node.additionalProperties !== true) {
484
+ const catchallType = this.transform(node.additionalProperties);
485
+ if (catchallType) result += `.catchall(${catchallType})`;
486
+ } else if (node.additionalProperties === true) result += `.catchall(${this.transform(ast.createSchema({ type: "unknown" }))})`;
487
+ else if (node.additionalProperties === false) result += ".strict()";
488
+ return result;
489
+ },
490
+ array(node) {
491
+ let result = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(ast.createSchema({ type: "unknown" }))})${lengthConstraints(node)}`;
492
+ if (node.unique) result += `.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`;
493
+ return result;
494
+ },
495
+ tuple(node) {
496
+ return `z.tuple([${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ")}])`;
497
+ },
498
+ union(node) {
499
+ const nodeMembers = node.members ?? [];
500
+ const members = nodeMembers.map((memberNode) => {
501
+ const member = this.transform(memberNode);
502
+ return member && node.strategy === "one" ? strictOneOfMember$1(member, memberNode) : member;
503
+ }).filter(Boolean);
504
+ if (members.length === 0) return "";
505
+ if (members.length === 1) return members[0];
506
+ if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, [${members.join(", ")}])`;
507
+ return `z.union([${members.join(", ")}])`;
508
+ },
509
+ intersection(node) {
510
+ const members = node.members ?? [];
511
+ if (members.length === 0) return "";
512
+ const [first, ...rest] = members;
513
+ if (!first) return "";
514
+ let base = this.transform(first);
515
+ if (!base) return "";
516
+ for (const member of rest) {
517
+ if (member.primitive === "string") {
518
+ const c = lengthConstraints(ast.narrowSchema(member, "string") ?? {});
519
+ if (c) {
520
+ base += c;
521
+ continue;
522
+ }
523
+ } else if (member.primitive === "number" || member.primitive === "integer") {
524
+ const c = numberConstraints(ast.narrowSchema(member, "number") ?? ast.narrowSchema(member, "integer") ?? {});
525
+ if (c) {
526
+ base += c;
527
+ continue;
528
+ }
529
+ } else if (member.primitive === "array") {
530
+ const c = lengthConstraints(ast.narrowSchema(member, "array") ?? {});
531
+ if (c) {
532
+ base += c;
533
+ continue;
534
+ }
535
+ }
536
+ const transformed = this.transform(member);
537
+ if (transformed) base = `${base}.and(${transformed})`;
538
+ }
539
+ return base;
540
+ },
541
+ ...options.nodes
542
+ },
543
+ print(node) {
544
+ const { keysToOmit } = this.options;
545
+ let base = this.transform(node);
546
+ if (!base) return null;
547
+ const meta = ast.syncSchemaRef(node);
548
+ if (keysToOmit?.length && meta.primitive === "object" && !(meta.type === "union" && meta.discriminatorPropertyName)) {
549
+ const lazyMatch = base.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
550
+ if (lazyMatch) base = `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }))`;
551
+ else base = `${base}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} })`;
552
+ }
553
+ return applyModifiers({
554
+ value: base,
555
+ nullable: meta.nullable,
556
+ optional: meta.optional,
557
+ nullish: meta.nullish,
558
+ defaultValue: meta.default,
559
+ description: meta.description
560
+ });
561
+ }
562
+ };
563
+ });
564
+ //#endregion
565
+ //#region src/printers/printerZodMini.ts
566
+ function strictOneOfMember(member, node) {
567
+ if (node.type === "object" && (node.additionalProperties === void 0 || node.additionalProperties === false)) return member.replace(/^z\.object\(/, "z.strictObject(");
568
+ return member;
569
+ }
570
+ /**
571
+ * Zod v4 **Mini** printer built with `definePrinter`.
572
+ *
573
+ * Converts a `SchemaNode` AST into a Zod v4 code string using the functional API
574
+ * (`z.optional(z.string())`) for improved tree-shaking. See {@link printerZod} for the chainable API.
575
+ *
576
+ * @example Functional Mini API
577
+ * ```ts
578
+ * const printer = printerZodMini({})
579
+ * const code = printer.print(optionalStringNode) // "z.optional(z.string())"
580
+ * ```
581
+ */
582
+ const printerZodMini = ast.definePrinter((options) => {
583
+ return {
584
+ name: "zod-mini",
585
+ options,
586
+ nodes: {
587
+ any: () => "z.any()",
588
+ unknown: () => "z.unknown()",
589
+ void: () => "z.void()",
590
+ never: () => "z.never()",
591
+ boolean: () => "z.boolean()",
592
+ null: () => "z.null()",
593
+ string(node) {
594
+ return `z.string()${lengthChecksMini(node)}`;
595
+ },
596
+ number(node) {
597
+ return `z.number()${numberChecksMini(node)}`;
598
+ },
599
+ integer(node) {
600
+ return `z.int()${numberChecksMini(node)}`;
601
+ },
602
+ bigint(node) {
603
+ return `z.bigint()${numberChecksMini(node)}`;
604
+ },
605
+ date(node) {
606
+ if (node.representation === "string") return "z.iso.date()";
607
+ return "z.date()";
608
+ },
609
+ datetime() {
610
+ return "z.string()";
611
+ },
612
+ time(node) {
613
+ if (node.representation === "string") return "z.iso.time()";
614
+ return "z.date()";
615
+ },
616
+ uuid(node) {
617
+ return `${this.options.guidType === "guid" ? "z.guid()" : "z.uuid()"}${lengthChecksMini(node)}`;
618
+ },
619
+ email(node) {
620
+ return `z.email()${lengthChecksMini(node)}`;
621
+ },
622
+ url(node) {
623
+ return `z.url()${lengthChecksMini(node)}`;
624
+ },
625
+ ipv4: () => "z.ipv4()",
626
+ ipv6: () => "z.ipv6()",
627
+ blob: () => "z.instanceof(File)",
628
+ enum(node) {
629
+ const nonNullValues = (node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? []).filter((v) => v !== null);
630
+ if (node.namedEnumValues?.length) {
631
+ const literals = nonNullValues.map((v) => `z.literal(${formatLiteral(v)})`);
632
+ if (literals.length === 1) return literals[0];
633
+ return `z.union([${literals.join(", ")}])`;
634
+ }
635
+ return `z.enum([${nonNullValues.map(formatLiteral).join(", ")}])`;
636
+ },
637
+ ref(node) {
638
+ if (!node.name) return void 0;
639
+ const refName = node.ref ? ast.extractRefName(node.ref) ?? node.name : node.name;
640
+ const resolvedName = node.ref ? this.options.resolver?.default(refName, "function") ?? refName : node.name;
641
+ if (node.ref && this.options.cyclicSchemas?.has(refName)) return `z.lazy(() => ${resolvedName})`;
642
+ return resolvedName;
643
+ },
644
+ object(node) {
645
+ return `z.object({\n ${node.properties.map((prop) => {
646
+ const { name: propName, schema } = prop;
647
+ const meta = ast.syncSchemaRef(schema);
648
+ const isNullable = meta.nullable;
649
+ const isOptional = schema.optional;
650
+ const isNullish = schema.nullish;
651
+ const hasSelfRef = this.options.cyclicSchemas != null && ast.containsCircularRef(schema, { circularSchemas: this.options.cyclicSchemas });
652
+ const savedCyclicSchemas = this.options.cyclicSchemas;
653
+ if (hasSelfRef) this.options.cyclicSchemas = void 0;
654
+ const baseOutput = this.transform(schema) ?? this.transform(ast.createSchema({ type: "unknown" }));
655
+ if (hasSelfRef) this.options.cyclicSchemas = savedCyclicSchemas;
656
+ const value = applyMiniModifiers({
657
+ value: this.options.wrapOutput ? this.options.wrapOutput({
658
+ output: baseOutput,
659
+ schema
660
+ }) || baseOutput : baseOutput,
661
+ nullable: isNullable,
662
+ optional: isOptional,
663
+ nullish: isNullish,
664
+ defaultValue: meta.default
665
+ });
666
+ if (hasSelfRef) return `get "${propName}"() { return ${value} }`;
667
+ return `"${propName}": ${value}`;
668
+ }).join(",\n ")}\n })`;
669
+ },
670
+ array(node) {
671
+ let result = `z.array(${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ") || this.transform(ast.createSchema({ type: "unknown" }))})${lengthChecksMini(node)}`;
672
+ if (node.unique) result += `.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`;
673
+ return result;
674
+ },
675
+ tuple(node) {
676
+ return `z.tuple([${(node.items ?? []).map((item) => this.transform(item)).filter(Boolean).join(", ")}])`;
677
+ },
678
+ union(node) {
679
+ const nodeMembers = node.members ?? [];
680
+ const members = nodeMembers.map((memberNode) => {
681
+ const member = this.transform(memberNode);
682
+ return member && node.strategy === "one" ? strictOneOfMember(member, memberNode) : member;
683
+ }).filter(Boolean);
684
+ if (members.length === 0) return "";
685
+ if (members.length === 1) return members[0];
686
+ if (node.discriminatorPropertyName && !nodeMembers.some((m) => m.type === "intersection")) return `z.discriminatedUnion(${stringify(node.discriminatorPropertyName)}, [${members.join(", ")}])`;
687
+ return `z.union([${members.join(", ")}])`;
688
+ },
689
+ intersection(node) {
690
+ const members = node.members ?? [];
691
+ if (members.length === 0) return "";
692
+ const [first, ...rest] = members;
693
+ if (!first) return "";
694
+ let base = this.transform(first);
695
+ if (!base) return "";
696
+ for (const member of rest) {
697
+ if (member.primitive === "string") {
698
+ const c = lengthChecksMini(ast.narrowSchema(member, "string") ?? {});
699
+ if (c) {
700
+ base += c;
701
+ continue;
702
+ }
703
+ } else if (member.primitive === "number" || member.primitive === "integer") {
704
+ const c = numberChecksMini(ast.narrowSchema(member, "number") ?? ast.narrowSchema(member, "integer") ?? {});
705
+ if (c) {
706
+ base += c;
707
+ continue;
708
+ }
709
+ } else if (member.primitive === "array") {
710
+ const c = lengthChecksMini(ast.narrowSchema(member, "array") ?? {});
711
+ if (c) {
712
+ base += c;
713
+ continue;
714
+ }
715
+ }
716
+ const transformed = this.transform(member);
717
+ if (transformed) base = `z.intersection(${base}, ${transformed})`;
718
+ }
719
+ return base;
720
+ },
721
+ ...options.nodes
722
+ },
723
+ print(node) {
724
+ const { keysToOmit } = this.options;
725
+ let base = this.transform(node);
726
+ if (!base) return null;
727
+ const meta = ast.syncSchemaRef(node);
728
+ if (keysToOmit?.length && meta.primitive === "object" && !(meta.type === "union" && meta.discriminatorPropertyName)) {
729
+ const lazyMatch = base.match(/^z\.lazy\(\(\)\s*=>\s*(.+)\)$/);
730
+ if (lazyMatch) base = `z.lazy(() => ${lazyMatch[1]}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} }))`;
731
+ else base = `${base}.omit({ ${keysToOmit.map((k) => `"${k}": true`).join(", ")} })`;
732
+ }
733
+ return applyMiniModifiers({
734
+ value: base,
735
+ nullable: meta.nullable,
736
+ optional: meta.optional,
737
+ nullish: meta.nullish,
738
+ defaultValue: meta.default
739
+ });
740
+ }
741
+ };
742
+ });
743
+ //#endregion
744
+ //#region src/generators/zodGenerator.tsx
745
+ const zodPrinterCache = /* @__PURE__ */ new WeakMap();
746
+ const zodMiniPrinterCache = /* @__PURE__ */ new WeakMap();
747
+ const zodGenerator = defineGenerator({
748
+ name: "zod",
749
+ renderer: jsxRendererSync,
750
+ schema(node, ctx) {
751
+ const { adapter, config, resolver, root, inputNode } = ctx;
752
+ const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, printer } = ctx.options;
753
+ const dateType = adapter.options.dateType;
754
+ if (!node.name) return;
755
+ const mode = ctx.getMode(output);
756
+ const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
757
+ const imports = adapter.getImports(node, (schemaName) => ({
758
+ name: resolver.resolveSchemaName(schemaName),
759
+ path: resolver.resolveFile({
760
+ name: schemaName,
761
+ extname: ".ts"
762
+ }, {
763
+ root,
764
+ output,
765
+ group
766
+ }).path
767
+ }));
768
+ const meta = {
769
+ name: resolver.resolveSchemaName(node.name),
770
+ file: resolver.resolveFile({
771
+ name: node.name,
772
+ extname: ".ts"
773
+ }, {
774
+ root,
775
+ output,
776
+ group
777
+ })
778
+ };
779
+ const inferTypeName = inferred ? resolver.resolveSchemaTypeName(node.name) : void 0;
780
+ const cyclicSchemas = ast.findCircularSchemas(inputNode.schemas);
781
+ const schemaPrinter = mini ? getCachedMiniPrinter() : getCachedStdPrinter();
782
+ function getCachedStdPrinter() {
783
+ const cached = zodPrinterCache.get(resolver);
784
+ if (cached && cached.coercion === coercion && cached.guidType === guidType && cached.dateType === dateType) return cached.printer;
785
+ const p = printerZod({
786
+ coercion,
787
+ guidType,
788
+ dateType,
789
+ wrapOutput,
790
+ resolver,
791
+ cyclicSchemas,
792
+ nodes: printer?.nodes
793
+ });
794
+ zodPrinterCache.set(resolver, {
795
+ printer: p,
796
+ coercion,
797
+ guidType,
798
+ dateType
799
+ });
800
+ return p;
801
+ }
802
+ function getCachedMiniPrinter() {
803
+ const cached = zodMiniPrinterCache.get(resolver);
804
+ if (cached && cached.guidType === guidType) return cached.printer;
805
+ const p = printerZodMini({
806
+ guidType,
807
+ wrapOutput,
808
+ resolver,
809
+ cyclicSchemas,
810
+ nodes: printer?.nodes
811
+ });
812
+ zodMiniPrinterCache.set(resolver, {
813
+ printer: p,
814
+ guidType
815
+ });
816
+ return p;
817
+ }
818
+ return /* @__PURE__ */ jsxs(File, {
819
+ baseName: meta.file.baseName,
820
+ path: meta.file.path,
821
+ meta: meta.file.meta,
822
+ banner: resolver.resolveBanner(inputNode, {
823
+ output,
824
+ config
825
+ }),
826
+ footer: resolver.resolveFooter(inputNode, {
827
+ output,
828
+ config
829
+ }),
830
+ children: [
831
+ /* @__PURE__ */ jsx(File.Import, {
832
+ name: isZodImport ? "z" : ["z"],
833
+ path: importPath,
834
+ isNameSpace: isZodImport
835
+ }),
836
+ mode === "split" && imports.map((imp) => /* @__PURE__ */ jsx(File.Import, {
837
+ root: meta.file.path,
838
+ path: imp.path,
839
+ name: imp.name
840
+ }, [node.name, imp.path].join("-"))),
841
+ /* @__PURE__ */ jsx(Zod, {
842
+ name: meta.name,
843
+ node,
844
+ printer: schemaPrinter,
845
+ inferTypeName
846
+ })
847
+ ]
848
+ });
849
+ },
850
+ operation(node, ctx) {
851
+ const { adapter, config, resolver, root, inputNode } = ctx;
852
+ const { output, coercion, guidType, mini, wrapOutput, inferred, importPath, group, paramsCasing, printer } = ctx.options;
853
+ const dateType = adapter.options.dateType;
854
+ const mode = ctx.getMode(output);
855
+ const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
856
+ const params = ast.caseParams(node.parameters, paramsCasing);
857
+ const meta = { file: resolver.resolveFile({
858
+ name: node.operationId,
859
+ extname: ".ts",
860
+ tag: node.tags[0] ?? "default",
861
+ path: node.path
862
+ }, {
863
+ root,
864
+ output,
865
+ group
866
+ }) };
867
+ const cyclicSchemas = ast.findCircularSchemas(inputNode.schemas);
868
+ function renderSchemaEntry({ schema, name, keysToOmit }) {
869
+ if (!schema) return null;
870
+ const inferTypeName = inferred ? resolver.resolveTypeName(name) : void 0;
871
+ const imports = adapter.getImports(schema, (schemaName) => ({
872
+ name: resolver.resolveSchemaName(schemaName),
873
+ path: resolver.resolveFile({
874
+ name: schemaName,
875
+ extname: ".ts"
876
+ }, {
877
+ root,
878
+ output,
879
+ group
880
+ }).path
881
+ }));
882
+ const cachedStd = zodPrinterCache.get(resolver);
883
+ const cachedMini = zodMiniPrinterCache.get(resolver);
884
+ const schemaPrinter = mini ? keysToOmit?.length ? printerZodMini({
885
+ guidType,
886
+ wrapOutput,
887
+ resolver,
888
+ keysToOmit,
889
+ cyclicSchemas,
890
+ nodes: printer?.nodes
891
+ }) : cachedMini?.guidType === guidType ? cachedMini.printer : printerZodMini({
892
+ guidType,
893
+ wrapOutput,
894
+ resolver,
895
+ cyclicSchemas,
896
+ nodes: printer?.nodes
897
+ }) : keysToOmit?.length ? printerZod({
898
+ coercion,
899
+ guidType,
900
+ dateType,
901
+ wrapOutput,
902
+ resolver,
903
+ keysToOmit,
904
+ cyclicSchemas,
905
+ nodes: printer?.nodes
906
+ }) : cachedStd?.coercion === coercion && cachedStd?.guidType === guidType && cachedStd?.dateType === dateType ? cachedStd.printer : printerZod({
907
+ coercion,
908
+ guidType,
909
+ dateType,
910
+ wrapOutput,
911
+ resolver,
912
+ cyclicSchemas,
913
+ nodes: printer?.nodes
914
+ });
915
+ return /* @__PURE__ */ jsxs(Fragment, { children: [mode === "split" && imports.map((imp) => /* @__PURE__ */ jsx(File.Import, {
916
+ root: meta.file.path,
917
+ path: imp.path,
918
+ name: imp.name
919
+ }, [
920
+ name,
921
+ imp.path,
922
+ imp.name
923
+ ].join("-"))), /* @__PURE__ */ jsx(Zod, {
924
+ name,
925
+ node: schema,
926
+ printer: schemaPrinter,
927
+ inferTypeName
928
+ })] });
929
+ }
930
+ const paramSchemas = params.map((param) => renderSchemaEntry({
931
+ schema: param.schema,
932
+ name: resolver.resolveParamName(node, param)
933
+ }));
934
+ const responseSchemas = node.responses.map((res) => renderSchemaEntry({
935
+ schema: res.schema,
936
+ name: resolver.resolveResponseStatusName(node, res.statusCode),
937
+ keysToOmit: res.keysToOmit
938
+ }));
939
+ const responsesWithSchema = node.responses.filter((res) => res.schema);
940
+ const responseUnionSchema = responsesWithSchema.length > 0 ? (() => {
941
+ const responseUnionName = resolver.resolveResponseName(node);
942
+ if (new Set(responsesWithSchema.flatMap((res) => res.schema ? adapter.getImports(res.schema, (schemaName) => ({
943
+ name: resolver.resolveSchemaName(schemaName),
944
+ path: ""
945
+ })).flatMap((imp) => Array.isArray(imp.name) ? imp.name : [imp.name]) : [])).has(responseUnionName)) return null;
946
+ const members = responsesWithSchema.map((res) => ast.createSchema({
947
+ type: "ref",
948
+ name: resolver.resolveResponseStatusName(node, res.statusCode)
949
+ }));
950
+ return renderSchemaEntry({
951
+ schema: members.length === 1 ? members[0] : ast.createSchema({
952
+ type: "union",
953
+ members
954
+ }),
955
+ name: responseUnionName
956
+ });
957
+ })() : null;
958
+ const requestSchema = node.requestBody?.content?.[0]?.schema ? renderSchemaEntry({
959
+ schema: {
960
+ ...node.requestBody.content[0].schema,
961
+ description: node.requestBody.description ?? node.requestBody.content[0].schema.description
962
+ },
963
+ name: resolver.resolveDataName(node),
964
+ keysToOmit: node.requestBody.content[0].keysToOmit
965
+ }) : null;
966
+ return /* @__PURE__ */ jsxs(File, {
967
+ baseName: meta.file.baseName,
968
+ path: meta.file.path,
969
+ meta: meta.file.meta,
970
+ banner: resolver.resolveBanner(inputNode, {
971
+ output,
972
+ config
973
+ }),
974
+ footer: resolver.resolveFooter(inputNode, {
975
+ output,
976
+ config
977
+ }),
978
+ children: [
979
+ /* @__PURE__ */ jsx(File.Import, {
980
+ name: isZodImport ? "z" : ["z"],
981
+ path: importPath,
982
+ isNameSpace: isZodImport
983
+ }),
984
+ paramSchemas,
985
+ responseSchemas,
986
+ responseUnionSchema,
987
+ requestSchema
988
+ ]
989
+ });
990
+ },
991
+ operations(nodes, ctx) {
992
+ const { config, resolver, root, inputNode } = ctx;
993
+ const { output, importPath, group, operations, paramsCasing } = ctx.options;
994
+ if (!operations) return;
995
+ const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
996
+ const meta = { file: resolver.resolveFile({
997
+ name: "operations",
998
+ extname: ".ts"
999
+ }, {
1000
+ root,
1001
+ output,
1002
+ group
1003
+ }) };
1004
+ const transformedOperations = nodes.map((node) => {
1005
+ return {
1006
+ node,
1007
+ data: buildSchemaNames(node, {
1008
+ params: ast.caseParams(node.parameters, paramsCasing),
1009
+ resolver
1010
+ })
1011
+ };
1012
+ });
1013
+ const imports = transformedOperations.flatMap(({ node, data }) => {
1014
+ const names = [
1015
+ data.request,
1016
+ ...Object.values(data.responses),
1017
+ ...Object.values(data.parameters)
1018
+ ].filter(Boolean);
1019
+ const opFile = resolver.resolveFile({
1020
+ name: node.operationId,
1021
+ extname: ".ts",
1022
+ tag: node.tags[0] ?? "default",
1023
+ path: node.path
1024
+ }, {
1025
+ root,
1026
+ output,
1027
+ group
1028
+ });
1029
+ return names.map((name) => /* @__PURE__ */ jsx(File.Import, {
1030
+ name: [name],
1031
+ root: meta.file.path,
1032
+ path: opFile.path
1033
+ }, [name, opFile.path].join("-")));
1034
+ });
1035
+ return /* @__PURE__ */ jsxs(File, {
1036
+ baseName: meta.file.baseName,
1037
+ path: meta.file.path,
1038
+ meta: meta.file.meta,
1039
+ banner: resolver.resolveBanner(inputNode, {
1040
+ output,
1041
+ config
1042
+ }),
1043
+ footer: resolver.resolveFooter(inputNode, {
1044
+ output,
1045
+ config
1046
+ }),
1047
+ children: [
1048
+ /* @__PURE__ */ jsx(File.Import, {
1049
+ isTypeOnly: true,
1050
+ name: isZodImport ? "z" : ["z"],
1051
+ path: importPath,
1052
+ isNameSpace: isZodImport
1053
+ }),
1054
+ imports,
1055
+ /* @__PURE__ */ jsx(Operations, {
1056
+ name: "operations",
1057
+ operations: transformedOperations
1058
+ })
1059
+ ]
1060
+ });
1061
+ }
1062
+ });
1063
+ //#endregion
1064
+ //#region src/resolvers/resolverZod.ts
1065
+ /**
1066
+ * Naming convention resolver for Zod plugin.
1067
+ *
1068
+ * Provides default naming helpers using camelCase with a `Schema` suffix for schemas.
1069
+ *
1070
+ * @example
1071
+ * `resolverZod.default('list pets', 'function') // → 'listPetsSchema'`
1072
+ */
1073
+ const resolverZod = defineResolver(() => {
1074
+ return {
1075
+ name: "default",
1076
+ pluginName: "plugin-zod",
1077
+ default(name, type) {
1078
+ return camelCase(name, {
1079
+ isFile: type === "file",
1080
+ suffix: type ? "schema" : void 0
1081
+ });
1082
+ },
1083
+ resolveSchemaName(name) {
1084
+ return camelCase(name, { suffix: "schema" });
1085
+ },
1086
+ resolveSchemaTypeName(name) {
1087
+ return pascalCase(name, { suffix: "schema" });
1088
+ },
1089
+ resolveTypeName(name) {
1090
+ return pascalCase(name);
1091
+ },
1092
+ resolvePathName(name, type) {
1093
+ return this.default(name, type);
1094
+ },
1095
+ resolveParamName(node, param) {
1096
+ return this.resolveSchemaName(`${node.operationId} ${param.in} ${param.name}`);
1097
+ },
1098
+ resolveResponseStatusName(node, statusCode) {
1099
+ return this.resolveSchemaName(`${node.operationId} Status ${statusCode}`);
1100
+ },
1101
+ resolveDataName(node) {
1102
+ return this.resolveSchemaName(`${node.operationId} Data`);
1103
+ },
1104
+ resolveResponsesName(node) {
1105
+ return this.resolveSchemaName(`${node.operationId} Responses`);
1106
+ },
1107
+ resolveResponseName(node) {
1108
+ return this.resolveSchemaName(`${node.operationId} Response`);
1109
+ },
1110
+ resolvePathParamsName(node, param) {
1111
+ return this.resolveParamName(node, param);
1112
+ },
1113
+ resolveQueryParamsName(node, param) {
1114
+ return this.resolveParamName(node, param);
1115
+ },
1116
+ resolveHeaderParamsName(node, param) {
1117
+ return this.resolveParamName(node, param);
1118
+ }
1119
+ };
1120
+ });
1121
+ //#endregion
63
1122
  //#region src/plugin.ts
1123
+ /**
1124
+ * Canonical plugin name for `@kubb/plugin-zod`, used in driver lookups and warnings.
1125
+ */
64
1126
  const pluginZodName = "plugin-zod";
65
- const pluginZod = createPlugin((options) => {
1127
+ /**
1128
+ * Generates Zod validation schemas from an OpenAPI specification.
1129
+ * Walks schemas and operations, delegates to generators, and writes barrel files
1130
+ * based on the configured `barrelType`.
1131
+ *
1132
+ * @example Zod schema generator
1133
+ * ```ts
1134
+ * import pluginZod from '@kubb/plugin-zod'
1135
+ * export default defineConfig({
1136
+ * plugins: [pluginZod({ output: { path: 'zod' } })]
1137
+ * })
1138
+ * ```
1139
+ */
1140
+ const pluginZod = definePlugin((options) => {
66
1141
  const { output = {
67
1142
  path: "zod",
68
1143
  barrelType: "named"
69
- }, group, exclude = [], include, override = [], transformers = {}, dateType = "string", unknownType = "any", emptySchemaType = unknownType, integerType = "number", typed = false, mapper = {}, operations = false, mini = false, version = mini ? "4" : satisfiesDependency("zod", ">=4") ? "4" : "3", guidType = "uuid", importPath = mini ? "zod/mini" : version === "4" ? "zod/v4" : "zod", coercion = false, inferred = false, generators = [zodGenerator, operations ? operationsGenerator : void 0].filter(Boolean), wrapOutput = void 0, contentType } = options;
1144
+ }, group, exclude = [], include, override = [], typed = false, operations = false, mini = false, guidType = "uuid", importPath = mini ? "zod/mini" : "zod", coercion = false, inferred = false, wrapOutput = void 0, paramsCasing, printer, resolver: userResolver, transformer: userTransformer, generators: userGenerators = [] } = options;
1145
+ const groupConfig = group ? {
1146
+ ...group,
1147
+ name: (ctx) => {
1148
+ if (group.type === "path") return `${ctx.group.split("/")[1]}`;
1149
+ return `${camelCase(ctx.group)}Controller`;
1150
+ }
1151
+ } : void 0;
70
1152
  return {
71
1153
  name: pluginZodName,
72
- options: {
73
- output,
74
- transformers,
75
- include,
76
- exclude,
77
- override,
78
- typed,
79
- dateType,
80
- unknownType,
81
- emptySchemaType,
82
- integerType,
83
- mapper,
84
- importPath,
85
- coercion,
86
- operations,
87
- inferred,
88
- group,
89
- wrapOutput,
90
- version,
91
- guidType,
92
- mini,
93
- usedEnumNames: {}
94
- },
95
- pre: [pluginOasName, typed ? pluginTsName : void 0].filter(Boolean),
96
- resolvePath(baseName, pathMode, options) {
97
- const root = path.resolve(this.config.root, this.config.output.path);
98
- if ((pathMode ?? getMode(path.resolve(root, output.path))) === "single")
99
- /**
100
- * when output is a file then we will always append to the same file(output file), see fileManager.addOrAppend
101
- * Other plugins then need to call addOrAppend instead of just add from the fileManager class
102
- */
103
- return path.resolve(root, output.path);
104
- if (group && (options?.group?.path || options?.group?.tag)) {
105
- const groupName = group?.name ? group.name : (ctx) => {
106
- if (group?.type === "path") return `${ctx.group.split("/")[1]}`;
107
- return `${camelCase(ctx.group)}Controller`;
108
- };
109
- return path.resolve(root, output.path, groupName({ group: group.type === "path" ? options.group.path : options.group.tag }), baseName);
110
- }
111
- return path.resolve(root, output.path, baseName);
112
- },
113
- resolveName(name, type) {
114
- let resolvedName = camelCase(name, {
115
- suffix: type ? "schema" : void 0,
116
- isFile: type === "file"
117
- });
118
- if (type === "type") resolvedName = pascalCase(resolvedName);
119
- if (type) return transformers?.name?.(resolvedName, type) || resolvedName;
120
- return resolvedName;
121
- },
122
- async install() {
123
- const root = path.resolve(this.config.root, this.config.output.path);
124
- const mode = getMode(path.resolve(root, output.path));
125
- const oas = await this.getOas();
126
- if (this.plugin.options.typed && this.plugin.options.version === "3") await this.addFile({
127
- baseName: "ToZod.ts",
128
- path: path.resolve(root, ".kubb/ToZod.ts"),
129
- sources: [{
130
- name: "ToZod",
131
- value: source
132
- }],
133
- imports: [],
134
- exports: []
135
- });
136
- const schemaFiles = await new SchemaGenerator(this.plugin.options, {
137
- fabric: this.fabric,
138
- oas,
139
- driver: this.driver,
140
- events: this.events,
141
- plugin: this.plugin,
142
- contentType,
143
- include: void 0,
144
- override,
145
- mode,
146
- output: output.path
147
- }).build(...generators);
148
- await this.upsertFile(...schemaFiles);
149
- const operationFiles = await new OperationGenerator(this.plugin.options, {
150
- fabric: this.fabric,
151
- oas,
152
- driver: this.driver,
153
- events: this.events,
154
- plugin: this.plugin,
155
- contentType,
1154
+ options,
1155
+ hooks: { "kubb:plugin:setup"(ctx) {
1156
+ ctx.setOptions({
1157
+ output,
156
1158
  exclude,
157
1159
  include,
158
1160
  override,
159
- mode
160
- }).build(...generators);
161
- await this.upsertFile(...operationFiles);
162
- const barrelFiles = await getBarrelFiles(this.fabric.files, {
163
- type: output.barrelType ?? "named",
164
- root,
165
- output,
166
- meta: { pluginName: this.plugin.name }
1161
+ group: groupConfig,
1162
+ typed,
1163
+ importPath,
1164
+ coercion,
1165
+ operations,
1166
+ inferred,
1167
+ guidType,
1168
+ mini,
1169
+ wrapOutput,
1170
+ paramsCasing,
1171
+ printer
167
1172
  });
168
- await this.upsertFile(...barrelFiles);
169
- }
1173
+ ctx.setResolver(userResolver ? {
1174
+ ...resolverZod,
1175
+ ...userResolver
1176
+ } : resolverZod);
1177
+ if (userTransformer) ctx.setTransformer(userTransformer);
1178
+ ctx.addGenerator(zodGenerator);
1179
+ for (const gen of userGenerators) ctx.addGenerator(gen);
1180
+ } }
170
1181
  };
171
1182
  });
172
1183
  //#endregion
173
- export { pluginZod, pluginZodName };
1184
+ export { pluginZod as default, pluginZod, pluginZodName, printerZod, printerZodMini, resolverZod, zodGenerator };
174
1185
 
175
1186
  //# sourceMappingURL=index.js.map