@kubb/plugin-zod 5.2.0-canary.20260922T120902 → 5.2.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.d.ts CHANGED
@@ -225,6 +225,20 @@ type ResolverZod = Resolver & {
225
225
  * `resolver.schema.inputTypeName('order') // → 'OrderInputSchemaType'`
226
226
  */
227
227
  inputTypeName(name: string): string;
228
+ /**
229
+ * Resolves the type guard function name for a schema.
230
+ *
231
+ * @example Type guard names
232
+ * `resolver.schema.isName('pet') // → 'isPet'`
233
+ */
234
+ isName(name: string): string;
235
+ /**
236
+ * Resolves the assertion function name for a schema.
237
+ *
238
+ * @example Assertion names
239
+ * `resolver.schema.assertName('pet') // → 'assertPet'`
240
+ */
241
+ assertName(name: string): string;
228
242
  };
229
243
  /**
230
244
  * Names for an operation's parameters: an individual parameter and the grouped path, query, and
@@ -395,6 +409,20 @@ type Options = OutputOptions & {
395
409
  * to the default `resolverZod`.
396
410
  */
397
411
  resolver?: ResolverPatch<ResolverZod>;
412
+ /**
413
+ * Generate TypeScript type guards (`is*`) and assertion functions (`assert*`) for schemas
414
+ * using Zod v4's native `validate` API.
415
+ * - `true`: Generates both `is*` type guards and `assert*` assertion functions.
416
+ * - `{ is?: boolean; assert?: boolean }`: Selectively enable type guards or assertions.
417
+ *
418
+ * @default false
419
+ * @note Only compatible with Zod v4.6.0 or above.
420
+ * @see https://zod.dev
421
+ */
422
+ typeGuards?: boolean | {
423
+ is?: boolean;
424
+ assert?: boolean;
425
+ };
398
426
  /**
399
427
  * Replace the Zod handler for a specific schema type (`'integer'`, `'date'`, ...).
400
428
  * When `mini: true`, overrides target the Zod Mini printer instead.
@@ -424,6 +452,7 @@ type ResolvedOptions = {
424
452
  guidType: NonNullable<Options['guidType']>;
425
453
  regexType: NonNullable<Options['regexType']>;
426
454
  mini: NonNullable<Options['mini']>;
455
+ typeGuards: NonNullable<Options['typeGuards']>;
427
456
  printer: Options['printer'];
428
457
  };
429
458
  type PluginZod = PluginFactoryOptions<'plugin-zod', Options, ResolvedOptions, ResolverZod>;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { t as __name } from "./rolldown-runtime-C0LytTxp.js";
2
2
  import { Resolver, ast, containsCircularRef, createResolver, defineGenerator, definePlugin, extractRefName, syncSchemaRef } from "kubb/kit";
3
- import { Const, File, Type, jsxRenderer } from "kubb/jsx";
3
+ import { Const, File, Function, Type, jsxRenderer } from "kubb/jsx";
4
4
  import { Fragment, jsx, jsxs } from "kubb/jsx/jsx-runtime";
5
5
  //#region ../../internals/shared/src/params.ts
6
6
  /**
@@ -735,31 +735,61 @@ function mapSchemaItems(node, transform) {
735
735
  }
736
736
  //#endregion
737
737
  //#region src/components/Zod.tsx
738
- function Zod({ name, node, printer, inferTypeName, cyclic }) {
738
+ function Zod({ name, node, printer, inferTypeName, typeGuards, isName, assertName, mini, cyclic }) {
739
739
  const output = printer.print(node);
740
740
  if (!output) return;
741
741
  const needsAnnotation = cyclic && node.type !== "object";
742
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(File.Source, {
743
- name,
744
- isExportable: true,
745
- isIndexable: true,
746
- children: /* @__PURE__ */ jsx(Const, {
747
- export: true,
742
+ const targetType = inferTypeName ?? `z.infer<typeof ${name}>`;
743
+ const shouldGenerateIs = isName && (typeof typeGuards === "object" ? typeGuards.is ?? true : Boolean(typeGuards));
744
+ const shouldGenerateAssert = assertName && (typeof typeGuards === "object" ? typeGuards.assert ?? true : Boolean(typeGuards));
745
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
746
+ /* @__PURE__ */ jsx(File.Source, {
748
747
  name,
749
- type: needsAnnotation ? "z.ZodType" : void 0,
750
- children: output
751
- })
752
- }), inferTypeName && /* @__PURE__ */ jsx(File.Source, {
753
- name: inferTypeName,
754
- isExportable: true,
755
- isIndexable: true,
756
- isTypeOnly: true,
757
- children: /* @__PURE__ */ jsx(Type, {
758
- export: true,
748
+ isExportable: true,
749
+ isIndexable: true,
750
+ children: /* @__PURE__ */ jsx(Const, {
751
+ export: true,
752
+ name,
753
+ type: needsAnnotation ? "z.ZodType" : void 0,
754
+ children: output
755
+ })
756
+ }),
757
+ inferTypeName && /* @__PURE__ */ jsx(File.Source, {
759
758
  name: inferTypeName,
760
- children: `z.infer<typeof ${name}>`
759
+ isExportable: true,
760
+ isIndexable: true,
761
+ isTypeOnly: true,
762
+ children: /* @__PURE__ */ jsx(Type, {
763
+ export: true,
764
+ name: inferTypeName,
765
+ children: `z.infer<typeof ${name}>`
766
+ })
767
+ }),
768
+ shouldGenerateIs && /* @__PURE__ */ jsx(File.Source, {
769
+ name: isName,
770
+ isExportable: true,
771
+ isIndexable: true,
772
+ children: /* @__PURE__ */ jsx(Const, {
773
+ export: true,
774
+ name: isName,
775
+ JSDoc: { comments: [`Type guard for {@link ${name}}`] },
776
+ children: mini ? `(data: unknown): data is ${targetType} => z.validate(${name}, data)` : `(data: unknown): data is ${targetType} => ${name}.validate(data)`
777
+ })
778
+ }),
779
+ shouldGenerateAssert && /* @__PURE__ */ jsx(File.Source, {
780
+ name: assertName,
781
+ isExportable: true,
782
+ isIndexable: true,
783
+ children: /* @__PURE__ */ jsx(Function, {
784
+ export: true,
785
+ name: assertName,
786
+ params: "data: unknown",
787
+ returnType: `asserts data is ${targetType}`,
788
+ JSDoc: { comments: [`Asserter for {@link ${name}}`, "@throws {z.ZodError} If data is invalid"] },
789
+ children: mini ? `if (!z.validate(${name}, data)) {\n z.parse(${name}, data)\n}` : `if (!${name}.validate(data)) {\n ${name}.parse(data)\n}`
790
+ })
761
791
  })
762
- })] });
792
+ ] });
763
793
  }
764
794
  //#endregion
765
795
  //#region src/constants.ts
@@ -1190,6 +1220,32 @@ const scalarNodes = {
1190
1220
  boolean: () => "z.boolean()",
1191
1221
  null: () => "z.null()",
1192
1222
  string(node) {
1223
+ if (!shouldCoerce(this.options.coercion, "strings")) {
1224
+ if (node.format === "byte" || node.format === "base64") return `z.base64()${lengthConstraints({
1225
+ ...node,
1226
+ regexType: this.options.regexType
1227
+ })}`;
1228
+ if (node.format === "base64url") return `z.base64url()${lengthConstraints({
1229
+ ...node,
1230
+ regexType: this.options.regexType
1231
+ })}`;
1232
+ if (node.format === "jwt") return `z.jwt()${lengthConstraints({
1233
+ ...node,
1234
+ regexType: this.options.regexType
1235
+ })}`;
1236
+ if (node.format === "ulid") return `z.ulid()${lengthConstraints({
1237
+ ...node,
1238
+ regexType: this.options.regexType
1239
+ })}`;
1240
+ if (node.format === "iban") return `z.iban()${lengthConstraints({
1241
+ ...node,
1242
+ regexType: this.options.regexType
1243
+ })}`;
1244
+ if (node.format === "duration") return `z.iso.duration()${lengthConstraints({
1245
+ ...node,
1246
+ regexType: this.options.regexType
1247
+ })}`;
1248
+ }
1193
1249
  const base = shouldCoerce(this.options.coercion, "strings") ? "z.coerce.string()" : "z.string()";
1194
1250
  const pattern = node.pattern ?? integerFormatPattern(node.format);
1195
1251
  return `${base}${lengthConstraints({
@@ -1202,6 +1258,10 @@ const scalarNodes = {
1202
1258
  return `${shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.number()" : "z.number()"}${numberConstraints(node)}`;
1203
1259
  },
1204
1260
  integer(node) {
1261
+ if (!shouldCoerce(this.options.coercion, "numbers")) {
1262
+ if (node.format === "int32") return `z.int32()${numberConstraints(node)}`;
1263
+ if (node.format === "uint32") return `z.uint32()${numberConstraints(node)}`;
1264
+ }
1205
1265
  return `${shouldCoerce(this.options.coercion, "numbers") ? "z.coerce.number().int()" : "z.int()"}${numberConstraints(node)}`;
1206
1266
  },
1207
1267
  bigint() {
@@ -1451,6 +1511,30 @@ const printerZodMini = ast.createPrinter((options) => {
1451
1511
  boolean: () => "z.boolean()",
1452
1512
  null: () => "z.null()",
1453
1513
  string(node) {
1514
+ if (node.format === "byte" || node.format === "base64") return `z.base64()${lengthChecksMini({
1515
+ ...node,
1516
+ regexType: this.options.regexType
1517
+ })}`;
1518
+ if (node.format === "base64url") return `z.base64url()${lengthChecksMini({
1519
+ ...node,
1520
+ regexType: this.options.regexType
1521
+ })}`;
1522
+ if (node.format === "jwt") return `z.jwt()${lengthChecksMini({
1523
+ ...node,
1524
+ regexType: this.options.regexType
1525
+ })}`;
1526
+ if (node.format === "ulid") return `z.ulid()${lengthChecksMini({
1527
+ ...node,
1528
+ regexType: this.options.regexType
1529
+ })}`;
1530
+ if (node.format === "iban") return `z.iban()${lengthChecksMini({
1531
+ ...node,
1532
+ regexType: this.options.regexType
1533
+ })}`;
1534
+ if (node.format === "duration") return `z.iso.duration()${lengthChecksMini({
1535
+ ...node,
1536
+ regexType: this.options.regexType
1537
+ })}`;
1454
1538
  const pattern = node.pattern ?? integerFormatPattern(node.format);
1455
1539
  return `z.string()${lengthChecksMini({
1456
1540
  ...node,
@@ -1462,6 +1546,8 @@ const printerZodMini = ast.createPrinter((options) => {
1462
1546
  return `z.number()${numberChecksMini(node)}`;
1463
1547
  },
1464
1548
  integer(node) {
1549
+ if (node.format === "int32") return `z.int32()${numberChecksMini(node)}`;
1550
+ if (node.format === "uint32") return `z.uint32()${numberChecksMini(node)}`;
1465
1551
  return `z.int()${numberChecksMini(node)}`;
1466
1552
  },
1467
1553
  bigint(node) {
@@ -1667,7 +1753,7 @@ const zodGenerator = defineGenerator({
1667
1753
  renderer: jsxRenderer,
1668
1754
  schema(node, ctx) {
1669
1755
  const { adapter, config, resolver, root } = ctx;
1670
- const { output, coercion, guidType, regexType, mini, inferred, importPath, group, printer } = ctx.options;
1756
+ const { output, coercion, guidType, regexType, mini, inferred, typeGuards, importPath, group, printer } = ctx.options;
1671
1757
  const dateType = getOasAdapter(adapter).options.dateType;
1672
1758
  if (!node.name) return;
1673
1759
  const isZodImport = ZOD_NAMESPACE_IMPORTS.has(importPath);
@@ -1723,6 +1809,8 @@ const zodGenerator = defineGenerator({
1723
1809
  })
1724
1810
  };
1725
1811
  const inferTypeName = inferred ? resolver.schema.typeName(node.name) : null;
1812
+ const isName = typeGuards ? resolver.schema.isName(node.name) : null;
1813
+ const assertName = typeGuards ? resolver.schema.assertName(node.name) : null;
1726
1814
  const stdPrinters = mini ? null : getStdPrinters(resolver, {
1727
1815
  coercion,
1728
1816
  guidType,
@@ -1777,6 +1865,10 @@ const zodGenerator = defineGenerator({
1777
1865
  node,
1778
1866
  printer: schemaPrinter,
1779
1867
  inferTypeName,
1868
+ typeGuards,
1869
+ isName,
1870
+ assertName,
1871
+ mini,
1780
1872
  cyclic: cyclicSchemas.has(node.name)
1781
1873
  }),
1782
1874
  hasDirectionalNode && stdPrinters && /* @__PURE__ */ jsx(Zod, {
@@ -1784,6 +1876,10 @@ const zodGenerator = defineGenerator({
1784
1876
  node,
1785
1877
  printer: stdPrinters.encode,
1786
1878
  inferTypeName: inferred ? resolver.schema.inputTypeName(node.name) : null,
1879
+ typeGuards,
1880
+ isName: typeGuards ? resolver.schema.isName(resolver.schema.inputName(node.name)) : null,
1881
+ assertName: typeGuards ? resolver.schema.assertName(resolver.schema.inputName(node.name)) : null,
1882
+ mini,
1787
1883
  cyclic: cyclicSchemas.has(node.name)
1788
1884
  })
1789
1885
  ]
@@ -2062,6 +2158,12 @@ const resolverZod = createResolver({
2062
2158
  },
2063
2159
  inputTypeName(name) {
2064
2160
  return this.schema.typeName(`${name} input`);
2161
+ },
2162
+ isName(name) {
2163
+ return ensureValidVarName(camelCase(name, { prefix: "is" }));
2164
+ },
2165
+ assertName(name) {
2166
+ return ensureValidVarName(camelCase(name, { prefix: "assert" }));
2065
2167
  }
2066
2168
  },
2067
2169
  param: createOperationParamResolver(),
@@ -2112,7 +2214,7 @@ const pluginZod = definePlugin((options) => {
2112
2214
  const { output = {
2113
2215
  path: "zod",
2114
2216
  barrel: { type: "named" }
2115
- }, group, exclude = [], include, override = [], mini = false, guidType = "uuid", regexType = "literal", importPath = mini ? "zod/mini" : "zod", coercion = false, inferred = false, printer, resolver: userResolver, macros: userMacros } = options;
2217
+ }, group, exclude = [], include, override = [], mini = false, guidType = "uuid", regexType = "literal", importPath = mini ? "zod/mini" : "zod", coercion = false, inferred = false, typeGuards = false, printer, resolver: userResolver, macros: userMacros } = options;
2116
2218
  const groupConfig = createGroupConfig(group);
2117
2219
  return {
2118
2220
  name: pluginZodName,
@@ -2130,6 +2232,7 @@ const pluginZod = definePlugin((options) => {
2130
2232
  guidType,
2131
2233
  regexType,
2132
2234
  mini,
2235
+ typeGuards,
2133
2236
  printer
2134
2237
  });
2135
2238
  ctx.setResolver(userResolver ? Resolver.merge(resolverZod, userResolver) : resolverZod);