@kubb/plugin-zod 5.4.0-canary.20260926T081722 → 5.4.1

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.js CHANGED
@@ -737,6 +737,7 @@ function mapSchemaItems(node, transform) {
737
737
  //#region src/components/Zod.tsx
738
738
  function Zod({ name, node, printer, inferTypeName, typeGuards, isName, assertName, mini, cyclic, compile }) {
739
739
  const output = printer.print(node);
740
+ const printerImports = printer.drainImports();
740
741
  if (!output) return;
741
742
  const needsAnnotation = cyclic && node.type !== "object";
742
743
  const isBare = ast.isBareRef(node, printer.options.keysToOmit);
@@ -746,6 +747,17 @@ function Zod({ name, node, printer, inferTypeName, typeGuards, isName, assertNam
746
747
  const shouldGenerateIs = isName && (typeof typeGuards === "object" ? typeGuards.is ?? true : Boolean(typeGuards));
747
748
  const shouldGenerateAssert = assertName && (typeof typeGuards === "object" ? typeGuards.assert ?? true : Boolean(typeGuards));
748
749
  return /* @__PURE__ */ jsxs(Fragment, { children: [
750
+ printerImports.map((imp) => /* @__PURE__ */ jsx(File.Import, {
751
+ name: imp.name,
752
+ path: imp.path,
753
+ root: imp.root,
754
+ isTypeOnly: imp.isTypeOnly,
755
+ isNameSpace: imp.isNameSpace
756
+ }, [
757
+ name,
758
+ imp.path,
759
+ imp.name
760
+ ].join("-"))),
749
761
  /* @__PURE__ */ jsx(File.Source, {
750
762
  name,
751
763
  isExportable: true,
@@ -811,13 +823,38 @@ function shouldCoerce(coercion, type) {
811
823
  if (coercion === true) return true;
812
824
  return !!coercion[type];
813
825
  }
826
+ function hasPropertyNames(node) {
827
+ return "propertyNames" in node && Boolean(node.propertyNames);
828
+ }
829
+ /**
830
+ * Whether the schema node represents a closed or finite set of keys (e.g. enum, literal,
831
+ * or union of enums/literals).
832
+ *
833
+ * In Zod v4, passing a closed key schema to `z.record()` enforces exhaustiveness (all keys
834
+ * must exist in the input). OpenAPI 3.1 `propertyNames` only validates present keys without
835
+ * requiring all keys to exist, so closed key schemas use `z.partialRecord()` instead.
836
+ */
837
+ function isClosedKeySchema(node) {
838
+ if (!node) return false;
839
+ const resolved = syncSchemaRef(node);
840
+ if (resolved.type === "enum") return true;
841
+ if (resolved.type === "union") {
842
+ const members = resolved.members ?? [];
843
+ return members.length > 0 && members.every(isClosedKeySchema);
844
+ }
845
+ return false;
846
+ }
814
847
  /**
815
848
  * Whether the node is a plain inline object whose shape can be lifted into an `.extend({ … })`
816
849
  * argument. A catchall, `patternProperties`, or a nullable/optional wrapper cannot, so those stay
817
850
  * on `.and(…)`.
818
851
  */
819
852
  function isPlainInlineObject(node) {
820
- return node.type === "object" && !node.nullable && !node.optional && !node.nullish && node.additionalProperties === void 0 && !node.patternProperties;
853
+ if (node.type !== "object") return false;
854
+ if (node.nullable || node.optional || node.nullish) return false;
855
+ const hasAdditionalProperties = node.additionalProperties !== void 0;
856
+ const hasPatternProperties = Boolean(node.patternProperties);
857
+ return !hasAdditionalProperties && !hasPatternProperties && !hasPropertyNames(node);
821
858
  }
822
859
  /**
823
860
  * Whether a node renders as a bare Zod object — one that accepts `.extend(…)` and can be a
@@ -828,7 +865,12 @@ function isPlainInlineObject(node) {
828
865
  */
829
866
  function isObjectSchemaNode(node, cyclicSchemas) {
830
867
  if (node.nullable || node.optional || node.nullish) return false;
831
- if (node.type === "object") return true;
868
+ if (node.type === "object") {
869
+ const hasProperties = Boolean(node.properties?.length);
870
+ const hasTypedAdditionalProperties = Boolean(node.additionalProperties && node.additionalProperties !== true);
871
+ const hasPatternProperties = Boolean(node.patternProperties);
872
+ return !(!hasProperties && (hasTypedAdditionalProperties || hasPatternProperties || hasPropertyNames(node)));
873
+ }
832
874
  if (node.type === "ref") {
833
875
  const refName = ast.resolveRefName(node);
834
876
  if (refName && cyclicSchemas?.has(refName)) return false;
@@ -1165,7 +1207,8 @@ function variesByDirection({ node, printerOptions }) {
1165
1207
  direction
1166
1208
  },
1167
1209
  transform: () => null,
1168
- base: () => null
1210
+ base: () => null,
1211
+ import: () => {}
1169
1212
  };
1170
1213
  return handler.call(context, node);
1171
1214
  };
@@ -1202,6 +1245,7 @@ function containsDirectionalNode({ node, printerOptions, seen = /* @__PURE__ */
1202
1245
  if ("items" in node && node.items) children.push(...node.items);
1203
1246
  if ("members" in node && node.members) children.push(...node.members);
1204
1247
  if ("additionalProperties" in node && node.additionalProperties && node.additionalProperties !== true) children.push(node.additionalProperties);
1248
+ if ("propertyNames" in node && node.propertyNames) children.push(node.propertyNames);
1205
1249
  return children.some((child) => containsDirectionalNode({
1206
1250
  node: child,
1207
1251
  printerOptions,
@@ -1366,14 +1410,23 @@ const printerZod = ast.createPrinter((options) => {
1366
1410
  object(node) {
1367
1411
  const entries = node.properties ?? [];
1368
1412
  const patterns = node.patternProperties ? Object.entries(node.patternProperties) : [];
1413
+ const propertyNamesNode = "propertyNames" in node ? node.propertyNames : void 0;
1414
+ const propertyNamesKeySchema = propertyNamesNode ? this.transform(propertyNamesNode) : null;
1369
1415
  const isStrict = node.additionalProperties === false && patterns.length === 0;
1370
- const objectBase = `${isStrict ? "z.strictObject" : "z.object"}(${buildZodObjectShape(this, node)})`;
1416
+ const isLoose = node.additionalProperties === true;
1417
+ const objectBase = `${isStrict ? "z.strictObject" : isLoose ? "z.looseObject" : "z.object"}(${buildZodObjectShape(this, node)})`;
1371
1418
  return (() => {
1419
+ const recordFn = isClosedKeySchema(propertyNamesNode) ? "z.partialRecord" : "z.record";
1372
1420
  if (node.additionalProperties && node.additionalProperties !== true) {
1373
1421
  const catchallType = this.transform(node.additionalProperties);
1422
+ if (entries.length === 0) return catchallType ? `${recordFn}(${propertyNamesKeySchema || "z.string()"}, ${catchallType})` : objectBase;
1374
1423
  return catchallType ? `${objectBase}.catchall(${catchallType})` : objectBase;
1375
1424
  }
1376
- if (node.additionalProperties === true) return `${objectBase}.catchall(${this.transform(ast.factory.createSchema({ type: "unknown" }))})`;
1425
+ if (entries.length === 0 && patterns.length === 0 && propertyNamesKeySchema && !isStrict) {
1426
+ const unknownType = this.transform(ast.factory.createSchema({ type: "unknown" }));
1427
+ return `${recordFn}(${propertyNamesKeySchema}, ${unknownType})`;
1428
+ }
1429
+ if (node.additionalProperties === true) return objectBase;
1377
1430
  if (isStrict) return objectBase;
1378
1431
  if (patterns.length > 0) {
1379
1432
  const values = patterns.map(([, valueSchema]) => {
@@ -1629,13 +1682,19 @@ const printerZodMini = ast.createPrinter((options) => {
1629
1682
  object(node) {
1630
1683
  const entries = node.properties ?? [];
1631
1684
  const patterns = node.patternProperties ? Object.entries(node.patternProperties) : [];
1685
+ const propertyNamesNode = "propertyNames" in node ? node.propertyNames : void 0;
1686
+ const propertyNamesKeySchema = propertyNamesNode ? this.transform(propertyNamesNode) : null;
1632
1687
  const isStrict = node.additionalProperties === false && patterns.length === 0;
1633
- const objectBase = `${isStrict ? "z.strictObject" : "z.object"}(${buildZodMiniObjectShape(this, node)})`;
1688
+ const isLoose = node.additionalProperties === true;
1689
+ const objectBase = `${isStrict ? "z.strictObject" : isLoose ? "z.looseObject" : "z.object"}(${buildZodMiniObjectShape(this, node)})`;
1690
+ const recordFn = isClosedKeySchema(propertyNamesNode) ? "z.partialRecord" : "z.record";
1634
1691
  if (node.additionalProperties && node.additionalProperties !== true) {
1635
1692
  const catchallType = this.transform(node.additionalProperties);
1693
+ if (entries.length === 0) return catchallType ? `${recordFn}(${propertyNamesKeySchema || "z.string()"}, ${catchallType})` : objectBase;
1636
1694
  return catchallType ? `z.catchall(${objectBase}, ${catchallType})` : objectBase;
1637
1695
  }
1638
- if (node.additionalProperties === true) return `z.catchall(${objectBase}, ${this.transform(ast.factory.createSchema({ type: "unknown" }))})`;
1696
+ if (entries.length === 0 && patterns.length === 0 && propertyNamesKeySchema && !isStrict) return `${recordFn}(${propertyNamesKeySchema}, ${this.transform(ast.factory.createSchema({ type: "unknown" }))})`;
1697
+ if (node.additionalProperties === true) return objectBase;
1639
1698
  if (isStrict) return objectBase;
1640
1699
  if (patterns.length > 0) {
1641
1700
  const values = patterns.map(([, valueSchema]) => {