@kubb/plugin-zod 5.8.0 → 5.8.2

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 CHANGED
@@ -56,6 +56,22 @@ function resolveContentTypeVariants(entries, baseName) {
56
56
  }
57
57
  const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
58
58
  /**
59
+ * An OpenAPI 3.2 `querystring` parameter is the whole query string, so a form-encoded object becomes one query parameter
60
+ * per property, typed and serialized like any other.
61
+ * Another content type, such as JSON, stays one parameter named after it, and the runtime sends its value as the whole query string.
62
+ */
63
+ function spreadQuerystring(param) {
64
+ const schema = param.schema.type === "ref" ? param.schema.schema : param.schema;
65
+ const formEncoded = !param.contentType || param.contentType.split(";")[0].trim().toLowerCase() === "application/x-www-form-urlencoded";
66
+ if (schema?.type !== "object" || !formEncoded) return [param];
67
+ return schema.properties.map((property) => _kubb_kit.ast.factory.createParameter({
68
+ name: property.name,
69
+ in: "query",
70
+ schema: property.schema,
71
+ required: param.required && property.required
72
+ }));
73
+ }
74
+ /**
59
75
  * Groups an operation's parameters by location (`path`/`query`/`header`/`cookie`), deduping each
60
76
  * group by name. Every plugin generator visiting the same `OperationNode` shares one AST instance
61
77
  * (see `KubbDriver`), so the result is cached per node to avoid re-filtering and re-deduping the
@@ -64,17 +80,18 @@ const operationParameterGroupsByNode = /* @__PURE__ */ new WeakMap();
64
80
  function getOperationParameters(node) {
65
81
  const cached = operationParameterGroupsByNode.get(node);
66
82
  if (cached) return cached;
67
- const group = (location) => {
83
+ const dedupe = (params) => {
68
84
  const seen = /* @__PURE__ */ new Set();
69
- return node.parameters.filter((param) => {
70
- if (param.in !== location || seen.has(param.name)) return false;
85
+ return params.filter((param) => {
86
+ if (seen.has(param.name)) return false;
71
87
  seen.add(param.name);
72
88
  return true;
73
89
  });
74
90
  };
91
+ const group = (location) => dedupe(node.parameters.filter((param) => param.in === location));
75
92
  const groups = {
76
93
  path: group("path"),
77
- query: group("query"),
94
+ query: dedupe([...group("query"), ...group("querystring").flatMap(spreadQuerystring)]),
78
95
  header: group("header"),
79
96
  cookie: group("cookie")
80
97
  };
@@ -1263,6 +1280,37 @@ function printWithOmit(ctx, node, modify, mini = false) {
1263
1280
  }
1264
1281
  // src/printers/zodNodes.ts
1265
1282
  /**
1283
+ * Reads a union of bare single-value enums as one enum, except under `xor` or an enum override, which need each member.
1284
+ * Only a numeric union with a `format` coerces, because it printed as `z.coerce.number()` before the adapter kept its values.
1285
+ */
1286
+ function getLiteralUnion({ node, options }) {
1287
+ if (node.strategy === "one" && options.oneOfType === "xor" || options.nodes?.enum) return void 0;
1288
+ const values = (node.members ?? []).map((member) => {
1289
+ const enumNode = _kubb_kit.ast.narrowSchema(member, _kubb_kit.ast.schemaTypes.enum);
1290
+ return enumNode && !enumNode.name && !enumNode.nullable && !enumNode.namedEnumValues && enumNode.enumValues?.length === 1 ? enumNode.enumValues[0] : void 0;
1291
+ });
1292
+ if (!values.length || !values.every((value) => value !== void 0 && value !== null)) return void 0;
1293
+ return {
1294
+ values,
1295
+ coerce: !!node.format && values.every((value) => typeof value === "number") && shouldCoerce(options.coercion, "numbers")
1296
+ };
1297
+ }
1298
+ /**
1299
+ * Whether a discriminated union member's discriminator property prints as a coerced literal union.
1300
+ */
1301
+ function hasCoercedDiscriminator({ member, propertyName, options }) {
1302
+ return _kubb_kit.ast.resolveSchemaProperties({
1303
+ node: member,
1304
+ propertyName
1305
+ }).some(({ schema }) => {
1306
+ const resolved = (0, _kubb_kit.syncSchemaRef)(schema);
1307
+ return resolved.type === "union" && !!getLiteralUnion({
1308
+ node: resolved,
1309
+ options
1310
+ })?.coerce;
1311
+ });
1312
+ }
1313
+ /**
1266
1314
  * Builds the scalar handlers both dialects share. They never recurse into children. The temporal
1267
1315
  * scalars and `bigint` differ per dialect, so each printer adds its own.
1268
1316
  */
@@ -1401,8 +1449,21 @@ function buildZodNodes(dialect, cyclicSchemas) {
1401
1449
  return `z.tuple(${buildList(mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(Boolean))})`;
1402
1450
  },
1403
1451
  union(node) {
1452
+ const literalUnion = getLiteralUnion({
1453
+ node,
1454
+ options: this.options
1455
+ });
1456
+ if (literalUnion) {
1457
+ const schema = buildEnum(literalUnion.values);
1458
+ return literalUnion.coerce ? dialect.coercedNumber(schema) : schema;
1459
+ }
1404
1460
  const nodeMembers = node.members ?? [];
1405
- const isDiscriminated = !!node.discriminatorPropertyName && nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemas));
1461
+ const { discriminatorPropertyName } = node;
1462
+ const isDiscriminated = !!discriminatorPropertyName && nodeMembers.every((m) => isObjectSchemaNode(m, cyclicSchemas)) && !nodeMembers.some((m) => hasCoercedDiscriminator({
1463
+ member: m,
1464
+ propertyName: discriminatorPropertyName,
1465
+ options: this.options
1466
+ }));
1406
1467
  const isXor = node.strategy === "one" && !isDiscriminated && this.options.oneOfType === "xor";
1407
1468
  const isStrict = node.strategy === "one" && !isXor && !unionsWithSiblings.has(node);
1408
1469
  const members = mapSchemaMembers(node, (memberNode) => this.transform(isStrict ? strictOneOfNode(memberNode) : memberNode)).map(({ schema, output }) => output && isStrict && dialect.strictMember ? dialect.strictMember(output, schema, cyclicSchemas) : output).filter(Boolean);
@@ -1588,6 +1649,7 @@ const standardDialect = {
1588
1649
  patternKey: patternKeySchema,
1589
1650
  objectShape: buildZodObjectShape,
1590
1651
  coercedInt: "z.coerce.number().int()",
1652
+ coercedNumber: (schema) => `z.coerce.number().pipe(${schema})`,
1591
1653
  catchall: (object, value) => `${object}.catchall(${value})`,
1592
1654
  nullable: (value) => `${value}.nullable()`,
1593
1655
  unique: (array) => `${array}.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" })`,
@@ -1696,6 +1758,7 @@ const miniDialect = {
1696
1758
  patternKey: patternKeySchemaMini,
1697
1759
  objectShape: buildZodMiniObjectShape,
1698
1760
  coercedInt: "z.coerce.number().check(z.int())",
1761
+ coercedNumber: (schema) => `z.pipe(z.coerce.number(), ${schema})`,
1699
1762
  catchall: (object, value) => `z.catchall(${object}, ${value})`,
1700
1763
  nullable: (value) => `z.nullable(${value})`,
1701
1764
  unique: (array) => `${array}.check(z.refine(items => new Set(items).size === items.length, { message: "Array entries must be unique" }))`,
@@ -2122,11 +2185,11 @@ const zodGenerator = (0, _kubb_kit.defineGenerator)({
2122
2185
  });
2123
2186
  // kubb-extension.json
2124
2187
  var compatibility = {
2125
- "kubb": ">=5.14.6",
2188
+ "kubb": ">=5.16.0",
2126
2189
  "node": ">=22.22"
2127
2190
  };
2128
2191
  // package.json
2129
- var version = "5.8.0";
2192
+ var version = "5.8.2";
2130
2193
  // src/resolvers/resolverZod.ts
2131
2194
  /**
2132
2195
  * Default resolver used by `@kubb/plugin-zod`. Decides the names and file