@kubb/ast 5.0.0-beta.27 → 5.0.0-beta.29

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
@@ -1663,12 +1663,16 @@ type MediaType = 'application/json' | 'application/xml' | 'application/x-www-for
1663
1663
  /**
1664
1664
  * AST node representing one operation response variant.
1665
1665
  *
1666
+ * Mirrors {@link OperationNode.requestBody}: the response body schemas live exclusively inside
1667
+ * the `content` array (one entry per content type), so the same schema is never duplicated at the
1668
+ * node root and inside `content`.
1669
+ *
1666
1670
  * @example
1667
1671
  * ```ts
1668
1672
  * const response: ResponseNode = {
1669
1673
  * kind: 'Response',
1670
1674
  * statusCode: '200',
1671
- * schema: createSchema({ type: 'string' }),
1675
+ * content: [{ contentType: 'application/json', schema: createSchema({ type: 'string' }) }],
1672
1676
  * }
1673
1677
  * ```
1674
1678
  */
@@ -1686,18 +1690,35 @@ type ResponseNode = BaseNode & {
1686
1690
  */
1687
1691
  description?: string;
1688
1692
  /**
1689
- * Response body schema.
1690
- */
1691
- schema: SchemaNode;
1692
- /**
1693
- * Response media type.
1694
- */
1695
- mediaType?: MediaType | null;
1696
- /**
1697
- * Property keys to exclude from the generated type via `Omit<Type, Keys>`.
1698
- * Set when a referenced schema has `writeOnly` fields that should not appear in response types.
1693
+ * All available content type entries for this response.
1694
+ *
1695
+ * When the adapter `contentType` option is set, this array contains exactly one entry for that
1696
+ * content type. Otherwise it contains one entry per content type declared in the spec, so that
1697
+ * plugins can generate a union of response types (e.g. `application/json` and `application/xml`).
1698
+ * Body-less responses keep a single entry whose `schema` is the empty/`void` placeholder.
1699
+ *
1700
+ * @example
1701
+ * ```ts
1702
+ * // spec response declares both application/json and application/xml
1703
+ * response.content[0].contentType // 'application/json'
1704
+ * response.content[1].contentType // 'application/xml'
1705
+ * ```
1699
1706
  */
1700
- keysToOmit?: Array<string> | null;
1707
+ content?: Array<{
1708
+ /**
1709
+ * The content type for this entry (e.g. `'application/json'`).
1710
+ */
1711
+ contentType: string;
1712
+ /**
1713
+ * Response body schema for this content type.
1714
+ */
1715
+ schema?: SchemaNode;
1716
+ /**
1717
+ * Property keys to exclude from the generated type via `Omit<Type, Keys>`.
1718
+ * Set when a referenced schema has `writeOnly` fields that should not appear in response types.
1719
+ */
1720
+ keysToOmit?: Array<string> | null;
1721
+ }>;
1701
1722
  };
1702
1723
  //#endregion
1703
1724
  //#region src/nodes/operation.d.ts
@@ -2314,16 +2335,23 @@ declare function createParameter(props: Pick<ParameterNode, 'name' | 'in' | 'sch
2314
2335
  /**
2315
2336
  * Creates a `ResponseNode`.
2316
2337
  *
2338
+ * Response body schemas live inside `content`. For convenience a single legacy `schema`
2339
+ * (with optional `mediaType`/`keysToOmit`) is normalized into one `content` entry, so the same
2340
+ * schema is never stored both at the node root and inside `content`.
2341
+ *
2317
2342
  * @example
2318
2343
  * ```ts
2319
2344
  * const response = createResponse({
2320
2345
  * statusCode: '200',
2321
- * description: 'Success',
2322
- * schema: createSchema({ type: 'object', properties: [] }),
2346
+ * content: [{ contentType: 'application/json', schema: createSchema({ type: 'object', properties: [] }) }],
2323
2347
  * })
2324
2348
  * ```
2325
2349
  */
2326
- declare function createResponse(props: Pick<ResponseNode, 'statusCode' | 'schema'> & Partial<Omit<ResponseNode, 'kind' | 'statusCode' | 'schema'>>): ResponseNode;
2350
+ declare function createResponse(props: Pick<ResponseNode, 'statusCode'> & Partial<Omit<ResponseNode, 'kind' | 'statusCode'>> & {
2351
+ schema?: SchemaNode;
2352
+ mediaType?: string | null;
2353
+ keysToOmit?: Array<string> | null;
2354
+ }): ResponseNode;
2327
2355
  /**
2328
2356
  * Creates a `FunctionParameterNode`.
2329
2357
  *
package/dist/index.js CHANGED
@@ -582,7 +582,9 @@ function* getChildren(node, recurse) {
582
582
  return;
583
583
  }
584
584
  if (node.kind === "Response") {
585
- if (node.schema) yield node.schema;
585
+ if (node.content) {
586
+ for (const c of node.content) if (c.schema) yield c.schema;
587
+ }
586
588
  return;
587
589
  }
588
590
  }
@@ -718,10 +720,13 @@ function transform(node, options) {
718
720
  const response = visitor.response?.(node, { parent }) ?? node;
719
721
  return {
720
722
  ...response,
721
- schema: transform(response.schema, {
722
- ...options,
723
- parent: response
724
- })
723
+ content: response.content?.map((entry) => ({
724
+ ...entry,
725
+ schema: entry.schema ? transform(entry.schema, {
726
+ ...options,
727
+ parent: response
728
+ }) : entry.schema
729
+ }))
725
730
  };
726
731
  }
727
732
  return node;
@@ -1190,6 +1195,11 @@ function combineImports(imports, exports, source) {
1190
1195
  if (!importNameMemo.has(key)) importNameMemo.set(key, n);
1191
1196
  return importNameMemo.get(key);
1192
1197
  };
1198
+ const pathsWithUsedNamedImport = /* @__PURE__ */ new Set();
1199
+ for (const node of imports) {
1200
+ if (!Array.isArray(node.name)) continue;
1201
+ if (node.name.some((item) => typeof item === "string" ? isUsed(item) : isUsed(item.name ?? item.propertyName))) pathsWithUsedNamedImport.add(node.path);
1202
+ }
1193
1203
  const result = [];
1194
1204
  const namedByPath = /* @__PURE__ */ new Map();
1195
1205
  const seen = /* @__PURE__ */ new Set();
@@ -1220,7 +1230,7 @@ function combineImports(imports, exports, source) {
1220
1230
  namedByPath.set(key, newItem);
1221
1231
  }
1222
1232
  } else {
1223
- if (name && !isUsed(name)) continue;
1233
+ if (name && !isUsed(name) && !pathsWithUsedNamedImport.has(path)) continue;
1224
1234
  const key = importKey(path, name, isTypeOnly);
1225
1235
  if (!seen.has(key)) {
1226
1236
  result.push(curr);
@@ -1636,19 +1646,28 @@ function createParameter(props) {
1636
1646
  /**
1637
1647
  * Creates a `ResponseNode`.
1638
1648
  *
1649
+ * Response body schemas live inside `content`. For convenience a single legacy `schema`
1650
+ * (with optional `mediaType`/`keysToOmit`) is normalized into one `content` entry, so the same
1651
+ * schema is never stored both at the node root and inside `content`.
1652
+ *
1639
1653
  * @example
1640
1654
  * ```ts
1641
1655
  * const response = createResponse({
1642
1656
  * statusCode: '200',
1643
- * description: 'Success',
1644
- * schema: createSchema({ type: 'object', properties: [] }),
1657
+ * content: [{ contentType: 'application/json', schema: createSchema({ type: 'object', properties: [] }) }],
1645
1658
  * })
1646
1659
  * ```
1647
1660
  */
1648
1661
  function createResponse(props) {
1662
+ const { schema, mediaType, keysToOmit, content, ...rest } = props;
1649
1663
  return {
1650
- ...props,
1651
- kind: "Response"
1664
+ ...rest,
1665
+ kind: "Response",
1666
+ content: content ?? (schema ? [{
1667
+ contentType: mediaType ?? "application/json",
1668
+ schema,
1669
+ keysToOmit: keysToOmit ?? null
1670
+ }] : void 0)
1652
1671
  };
1653
1672
  }
1654
1673
  /**