@kubb/plugin-ts 5.0.0-beta.56 → 5.0.0-beta.73

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
@@ -1,10 +1,10 @@
1
- import { t as __name } from "./chunk-C0LytTxp.js";
2
- import { extractRefName, jsStringEscape, stringify, trimQuotes } from "@kubb/ast/utils";
1
+ import { n as __name, t as __exportAll } from "./rolldown-runtime-CNktS9qV.js";
2
+ import { extractRefName, isStringType, jsStringEscape, mapSchemaItems, mapSchemaProperties, stringify, syncSchemaRef, trimQuotes } from "@kubb/ast/utils";
3
3
  import { parserTs } from "@kubb/parser-ts";
4
4
  import { File, jsxRenderer } from "@kubb/renderer-jsx";
5
- import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
6
5
  import ts from "typescript";
7
6
  import { Fragment, jsx, jsxs } from "@kubb/renderer-jsx/jsx-runtime";
7
+ import { ast, defineGenerator, definePlugin, defineResolver } from "@kubb/core";
8
8
  //#region ../../internals/utils/src/casing.ts
9
9
  /**
10
10
  * Shared implementation for camelCase and PascalCase conversion.
@@ -258,7 +258,59 @@ const PARAM_RANK = {
258
258
  };
259
259
  //#endregion
260
260
  //#region src/factory.ts
261
+ var factory_exports = /* @__PURE__ */ __exportAll({
262
+ SyntaxKind: () => SyntaxKind,
263
+ appendJSDocToNode: () => appendJSDocToNode,
264
+ buildIndexSignatures: () => buildIndexSignatures,
265
+ buildMemberNodes: () => buildMemberNodes,
266
+ buildPropertyType: () => buildPropertyType,
267
+ buildTupleNode: () => buildTupleNode,
268
+ constToTypeNode: () => constToTypeNode,
269
+ createArrayDeclaration: () => createArrayDeclaration,
270
+ createArrayTypeNode: () => createArrayTypeNode,
271
+ createEnumDeclaration: () => createEnumDeclaration,
272
+ createExportDeclaration: () => createExportDeclaration,
273
+ createFalse: () => createFalse,
274
+ createIdentifier: () => createIdentifier,
275
+ createImportDeclaration: () => createImportDeclaration,
276
+ createIndexSignature: () => createIndexSignature,
277
+ createInterfaceDeclaration: () => createInterfaceDeclaration,
278
+ createIntersectionDeclaration: () => createIntersectionDeclaration,
279
+ createJSDoc: () => createJSDoc,
280
+ createLiteralTypeNode: () => createLiteralTypeNode,
281
+ createNumericLiteral: () => createNumericLiteral,
282
+ createOmitDeclaration: () => createOmitDeclaration,
283
+ createOptionalTypeNode: () => createOptionalTypeNode,
284
+ createParameterSignature: () => createParameterSignature,
285
+ createPrefixUnaryExpression: () => createPrefixUnaryExpression,
286
+ createPropertySignature: () => createPropertySignature,
287
+ createQuestionToken: () => createQuestionToken,
288
+ createRestTypeNode: () => createRestTypeNode,
289
+ createStringLiteral: () => createStringLiteral,
290
+ createTrue: () => createTrue,
291
+ createTupleTypeNode: () => createTupleTypeNode,
292
+ createTypeAliasDeclaration: () => createTypeAliasDeclaration,
293
+ createTypeDeclaration: () => createTypeDeclaration,
294
+ createTypeLiteralNode: () => createTypeLiteralNode,
295
+ createTypeReferenceNode: () => createTypeReferenceNode,
296
+ createUnionDeclaration: () => createUnionDeclaration,
297
+ createUrlTemplateType: () => createUrlTemplateType,
298
+ dateOrStringNode: () => dateOrStringNode,
299
+ isNonNullable: () => isNonNullable$1,
300
+ keywordTypeNodes: () => keywordTypeNodes,
301
+ modifiers: () => modifiers,
302
+ syntaxKind: () => syntaxKind
303
+ });
261
304
  const { SyntaxKind, factory } = ts;
305
+ /**
306
+ * Compares two strings by UTF-16 code unit, keeping sorted output identical across platforms
307
+ * regardless of locale.
308
+ */
309
+ function compareStrings(a, b) {
310
+ if (a < b) return -1;
311
+ if (a > b) return 1;
312
+ return 0;
313
+ }
262
314
  function isNumber(value) {
263
315
  return typeof value === "number" && !Number.isNaN(value);
264
316
  }
@@ -366,6 +418,17 @@ function createParameterSignature(name, { modifiers, dotDotDotToken, questionTok
366
418
  return factory.createParameterDeclaration(modifiers, dotDotDotToken, name, createQuestionToken(questionToken), type, initializer);
367
419
  }
368
420
  /**
421
+ * Creates a JSDoc comment node from an array of comment strings.
422
+ * Returns null if no comments are provided.
423
+ */
424
+ function createJSDoc({ comments }) {
425
+ if (!comments.length) return null;
426
+ return factory.createJSDocComment(factory.createNodeArray(comments.map((comment, i) => {
427
+ if (i === comments.length - 1) return factory.createJSDocText(comment);
428
+ return factory.createJSDocText(`${comment}\n`);
429
+ })));
430
+ }
431
+ /**
369
432
  * Attaches JSDoc comments to an AST node as synthetic leading comments.
370
433
  * Filters out undefined comments before attaching.
371
434
  *
@@ -423,6 +486,50 @@ function createTypeDeclaration({ syntax, isExportable, comments, name, type }) {
423
486
  });
424
487
  }
425
488
  /**
489
+ * Creates an import declaration with support for default imports, named imports, namespace imports, and type-only imports.
490
+ * Optionally rename imported members with `propertyName` and `name` pairs.
491
+ *
492
+ * @example Default import
493
+ * `import Pet from './Pet'`
494
+ *
495
+ * @example Named imports with rename
496
+ * `import { Pet as Cat } from './Pet'`
497
+ *
498
+ * @example Namespace import
499
+ * `import * as Pet from './Pet'`
500
+ */
501
+ function createImportDeclaration({ name, path, isTypeOnly = false, isNameSpace = false }) {
502
+ if (!Array.isArray(name)) {
503
+ const importPropertyName = isNameSpace ? void 0 : factory.createIdentifier(name);
504
+ const importName = isNameSpace ? factory.createNamespaceImport(factory.createIdentifier(name)) : void 0;
505
+ return factory.createImportDeclaration(void 0, factory.createImportClause(isTypeOnly, importPropertyName, importName), factory.createStringLiteral(path), void 0);
506
+ }
507
+ const sortedName = name.toSorted((a, b) => compareStrings(typeof a === "object" ? a.propertyName : a, typeof b === "object" ? b.propertyName : b));
508
+ return factory.createImportDeclaration(void 0, factory.createImportClause(isTypeOnly, void 0, factory.createNamedImports(sortedName.map((item) => {
509
+ if (typeof item === "object") {
510
+ const obj = item;
511
+ if (obj.name) return factory.createImportSpecifier(false, factory.createIdentifier(obj.propertyName), factory.createIdentifier(obj.name));
512
+ return factory.createImportSpecifier(false, void 0, factory.createIdentifier(obj.propertyName));
513
+ }
514
+ return factory.createImportSpecifier(false, void 0, factory.createIdentifier(item));
515
+ }))), factory.createStringLiteral(path), void 0);
516
+ }
517
+ /**
518
+ * Creates an export declaration with support for named exports, namespace exports, and type-only exports.
519
+ * Sorts export names alphabetically for consistent output across platforms.
520
+ */
521
+ function createExportDeclaration({ path, asAlias, isTypeOnly = false, name }) {
522
+ if (name && !Array.isArray(name) && !asAlias) console.warn(`When using name as string, asAlias should be true ${name}`);
523
+ if (!Array.isArray(name)) {
524
+ const parsedName = name?.match(/^\d/) ? `_${name?.slice(1)}` : name;
525
+ return factory.createExportDeclaration(void 0, isTypeOnly, asAlias && parsedName ? factory.createNamespaceExport(factory.createIdentifier(parsedName)) : void 0, factory.createStringLiteral(path), void 0);
526
+ }
527
+ const sortedName = name.toSorted((a, b) => compareStrings(typeof a === "string" ? a : a.text, typeof b === "string" ? b : b.text));
528
+ return factory.createExportDeclaration(void 0, isTypeOnly, factory.createNamedExports(sortedName.map((propertyName) => {
529
+ return factory.createExportSpecifier(false, void 0, typeof propertyName === "string" ? factory.createIdentifier(propertyName) : propertyName);
530
+ })), factory.createStringLiteral(path), void 0);
531
+ }
532
+ /**
426
533
  * Apply casing transformation to enum keys
427
534
  */
428
535
  function applyEnumKeyCasing(key, casing = "none") {
@@ -454,7 +561,7 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
454
561
  return factory.createLiteralTypeNode(factory.createNumericLiteral(value?.toString()));
455
562
  }
456
563
  if (typeof value === "boolean") return factory.createLiteralTypeNode(value ? factory.createTrue() : factory.createFalse());
457
- if (value) return factory.createLiteralTypeNode(factory.createStringLiteral(value.toString()));
564
+ if (value !== null && value !== void 0) return factory.createLiteralTypeNode(factory.createStringLiteral(value.toString()));
458
565
  }).filter((node) => node !== void 0)))];
459
566
  if (type === "enum" || type === "constEnum") return [void 0, factory.createEnumDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword), type === "constEnum" ? factory.createToken(ts.SyntaxKind.ConstKeyword) : void 0].filter((modifier) => modifier !== void 0), factory.createIdentifier(typeName), enums.map(([key, value]) => {
460
567
  let initializer = factory.createStringLiteral(value?.toString());
@@ -465,7 +572,7 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
465
572
  const casingKey = applyEnumKeyCasing(`${typeName}_${key}`, enumKeyCasing);
466
573
  return factory.createEnumMember(propertyName(casingKey), initializer);
467
574
  }
468
- if (key) {
575
+ if (key !== null && key !== void 0) {
469
576
  const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
470
577
  return factory.createEnumMember(propertyName(casingKey), initializer);
471
578
  }
@@ -477,7 +584,7 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
477
584
  if (isNumber(value)) if (value < 0) initializer = factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)));
478
585
  else initializer = factory.createNumericLiteral(value);
479
586
  if (typeof value === "boolean") initializer = value ? factory.createTrue() : factory.createFalse();
480
- if (key) {
587
+ if (key !== null && key !== void 0) {
481
588
  const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
482
589
  return factory.createPropertyAssignment(propertyName(casingKey), initializer);
483
590
  }
@@ -638,12 +745,12 @@ function buildTupleNode(node, print) {
638
745
  /**
639
746
  * Applies `nullable` and optional/nullish `| undefined` union modifiers to a property's resolved base type.
640
747
  */
641
- function buildPropertyType(schema, baseType, optionalType) {
748
+ function buildPropertyType(schema, baseType, optionalType, optional) {
642
749
  const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(optionalType);
643
- const meta = ast.syncSchemaRef(schema);
750
+ const meta = syncSchemaRef(schema);
644
751
  let type = baseType;
645
752
  if (meta.nullable) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.null] });
646
- if ((meta.nullish || meta.optional) && addsUndefined) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.undefined] });
753
+ if ((optional || meta.nullish || meta.optional) && addsUndefined) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.undefined] });
647
754
  return type;
648
755
  }
649
756
  /**
@@ -757,6 +864,27 @@ function Type({ name, node, printer, enum: enumOptions, resolver }) {
757
864
  })] });
758
865
  }
759
866
  //#endregion
867
+ //#region ../../internals/shared/src/params.ts
868
+ const caseParamsCache = /* @__PURE__ */ new WeakMap();
869
+ /**
870
+ * Applies camelCase to parameter names and returns a new array without mutating the input.
871
+ *
872
+ * Run it before handing parameters to schema builders so output property keys get the right casing
873
+ * while `OperationNode.parameters` stays intact for other consumers. When `casing` is unset, the
874
+ * original array is returned unchanged. Results are cached per input array.
875
+ */
876
+ function caseParams(params, casing) {
877
+ if (!casing) return params;
878
+ const cached = caseParamsCache.get(params);
879
+ if (cached) return cached;
880
+ const result = params.map((param) => ({
881
+ ...param,
882
+ name: camelCase(param.name)
883
+ }));
884
+ caseParamsCache.set(params, result);
885
+ return result;
886
+ }
887
+ //#endregion
760
888
  //#region ../../internals/shared/src/operation.ts
761
889
  /**
762
890
  * Maps a content type to the PascalCase suffix used to name per-content-type variants
@@ -807,7 +935,7 @@ function resolveContentTypeVariants(entries, baseName) {
807
935
  });
808
936
  }
809
937
  function getOperationParameters(node, options = {}) {
810
- const params = ast.caseParams(node.parameters, options.paramsCasing);
938
+ const params = caseParams(node.parameters, options.paramsCasing === "original" ? void 0 : "camelcase");
811
939
  return {
812
940
  path: params.filter((param) => param.in === "path"),
813
941
  query: params.filter((param) => param.in === "query"),
@@ -855,11 +983,15 @@ function createGroupConfig(group) {
855
983
  * Constraint metadata (min/max length, pattern, multipleOf, min/maxProperties) is emitted as plain-text lines.
856
984
 
857
985
  */
858
- function buildPropertyJSDocComments(schema) {
859
- const meta = ast.syncSchemaRef(schema);
986
+ function isSchemaOptional(schema) {
987
+ return Boolean("optional" in schema && schema.optional || "nullish" in schema && schema.nullish);
988
+ }
989
+ function buildPropertyJSDocComments(schema, optional) {
990
+ const meta = syncSchemaRef(schema);
860
991
  const isArray = meta?.primitive === "array";
861
992
  const hasDescription = meta && "description" in meta && meta.description;
862
993
  const formatComment = meta && "format" in meta && meta.format ? hasDescription ? [" ", `Format: \`${meta.format}\``] : ["@description", `Format: \`${meta.format}\``] : [];
994
+ const exampleValues = meta?.examples ?? [];
863
995
  return [
864
996
  hasDescription ? `@description ${jsStringEscape(meta.description)}` : null,
865
997
  ...formatComment,
@@ -868,99 +1000,102 @@ function buildPropertyJSDocComments(schema) {
868
1000
  !isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
869
1001
  meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
870
1002
  meta && "default" in meta && meta.default !== void 0 ? `@default ${"primitive" in meta && meta.primitive === "string" ? stringify(meta.default) : meta.default}` : null,
871
- meta && "example" in meta && meta.example !== void 0 ? `@example ${meta.example}` : null,
872
- meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, "optional" in schema && schema.optional ? " | undefined" : null].filter(Boolean).join("") : null
1003
+ ...exampleValues.map((example) => `@example ${example}`),
1004
+ meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, optional ?? isSchemaOptional(schema) ? " | undefined" : null].filter(Boolean).join("") : null
873
1005
  ].filter(Boolean);
874
1006
  }
875
1007
  function buildParams(node, { params, resolver }) {
876
- return ast.createSchema({
1008
+ return ast.factory.createSchema({
877
1009
  type: "object",
878
- properties: params.map((param) => ast.createProperty({
1010
+ properties: params.map((param) => ast.factory.createProperty({
879
1011
  name: param.name,
880
1012
  required: param.required,
881
- schema: ast.createSchema({
1013
+ schema: ast.factory.createSchema({
882
1014
  type: "ref",
883
- name: resolver.resolveParamName(node, param)
1015
+ name: resolver.resolveParamName(node, param),
1016
+ optional: !param.required
884
1017
  })
885
1018
  }))
886
1019
  });
887
1020
  }
888
1021
  function buildData(node, { resolver }) {
889
- const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
890
- return ast.createSchema({
1022
+ const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node, { paramsCasing: "original" });
1023
+ const hasBody = Boolean(node.requestBody?.content?.[0]?.schema);
1024
+ const hasRequiredPath = pathParams.some((param) => param.required);
1025
+ const hasRequiredQuery = queryParams.some((param) => param.required);
1026
+ const hasRequiredHeader = headerParams.some((param) => param.required);
1027
+ return ast.factory.createSchema({
891
1028
  type: "object",
892
1029
  deprecated: node.deprecated,
893
1030
  properties: [
894
- ast.createProperty({
895
- name: "data",
896
- schema: node.requestBody?.content?.[0]?.schema ? ast.createSchema({
1031
+ ast.factory.createProperty({
1032
+ name: "body",
1033
+ required: hasBody,
1034
+ schema: hasBody ? ast.factory.createSchema({
897
1035
  type: "ref",
898
- name: resolver.resolveDataName(node),
899
- optional: true
900
- }) : ast.createSchema({
1036
+ name: resolver.resolveDataName(node)
1037
+ }) : ast.factory.createSchema({
901
1038
  type: "never",
902
1039
  primitive: void 0,
903
1040
  optional: true
904
1041
  })
905
1042
  }),
906
- ast.createProperty({
907
- name: "pathParams",
908
- required: pathParams.length > 0,
909
- schema: pathParams.length > 0 ? buildParams(node, {
910
- params: pathParams,
911
- resolver
912
- }) : ast.createSchema({
1043
+ ast.factory.createProperty({
1044
+ name: "path",
1045
+ required: hasRequiredPath,
1046
+ schema: pathParams.length > 0 ? ast.factory.createSchema({
1047
+ ...buildParams(node, {
1048
+ params: pathParams,
1049
+ resolver
1050
+ }),
1051
+ optional: !hasRequiredPath
1052
+ }) : ast.factory.createSchema({
913
1053
  type: "never",
914
- primitive: void 0
1054
+ primitive: void 0,
1055
+ optional: true
915
1056
  })
916
1057
  }),
917
- ast.createProperty({
918
- name: "queryParams",
919
- schema: queryParams.length > 0 ? ast.createSchema({
1058
+ ast.factory.createProperty({
1059
+ name: "query",
1060
+ required: hasRequiredQuery,
1061
+ schema: queryParams.length > 0 ? ast.factory.createSchema({
920
1062
  ...buildParams(node, {
921
1063
  params: queryParams,
922
1064
  resolver
923
1065
  }),
924
- optional: true
925
- }) : ast.createSchema({
1066
+ optional: !hasRequiredQuery
1067
+ }) : ast.factory.createSchema({
926
1068
  type: "never",
927
1069
  primitive: void 0,
928
1070
  optional: true
929
1071
  })
930
1072
  }),
931
- ast.createProperty({
932
- name: "headerParams",
933
- schema: headerParams.length > 0 ? ast.createSchema({
1073
+ ast.factory.createProperty({
1074
+ name: "headers",
1075
+ required: hasRequiredHeader,
1076
+ schema: headerParams.length > 0 ? ast.factory.createSchema({
934
1077
  ...buildParams(node, {
935
1078
  params: headerParams,
936
1079
  resolver
937
1080
  }),
938
- optional: true
939
- }) : ast.createSchema({
1081
+ optional: !hasRequiredHeader
1082
+ }) : ast.factory.createSchema({
940
1083
  type: "never",
941
1084
  primitive: void 0,
942
1085
  optional: true
943
1086
  })
944
- }),
945
- ast.createProperty({
946
- name: "url",
947
- required: true,
948
- schema: ast.createSchema({
949
- type: "url",
950
- path: node.path
951
- })
952
1087
  })
953
1088
  ]
954
1089
  });
955
1090
  }
956
1091
  function buildResponses(node, { resolver }) {
957
1092
  if (node.responses.length === 0) return null;
958
- return ast.createSchema({
1093
+ return ast.factory.createSchema({
959
1094
  type: "object",
960
- properties: node.responses.map((res) => ast.createProperty({
1095
+ properties: node.responses.map((res) => ast.factory.createProperty({
961
1096
  name: String(res.statusCode),
962
1097
  required: true,
963
- schema: ast.createSchema({
1098
+ schema: ast.factory.createSchema({
964
1099
  type: "ref",
965
1100
  name: resolver.resolveResponseStatusName(node, res.statusCode)
966
1101
  })
@@ -970,9 +1105,9 @@ function buildResponses(node, { resolver }) {
970
1105
  function buildResponseUnion(node, { resolver }) {
971
1106
  const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
972
1107
  if (responsesWithSchema.length === 0) return null;
973
- return ast.createSchema({
1108
+ return ast.factory.createSchema({
974
1109
  type: "union",
975
- members: responsesWithSchema.map((res) => ast.createSchema({
1110
+ members: responsesWithSchema.map((res) => ast.factory.createSchema({
976
1111
  type: "ref",
977
1112
  name: resolver.resolveResponseStatusName(node, res.statusCode)
978
1113
  }))
@@ -980,9 +1115,7 @@ function buildResponseUnion(node, { resolver }) {
980
1115
  }
981
1116
  //#endregion
982
1117
  //#region src/printers/printerTs.ts
983
- function isNonNullable(value) {
984
- return value !== null && value !== void 0;
985
- }
1118
+ const { isNonNullable } = factory_exports;
986
1119
  /**
987
1120
  * TypeScript type printer built with `definePrinter`.
988
1121
  *
@@ -1006,7 +1139,7 @@ function isNonNullable(value) {
1006
1139
  * const declaration = printer.print(schemaNode) // ts.TypeAliasDeclaration | ts.InterfaceDeclaration
1007
1140
  * ```
1008
1141
  */
1009
- const printerTs = ast.definePrinter((options) => {
1142
+ const printerTs = ast.createPrinter((options) => {
1010
1143
  const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(options.optionalType);
1011
1144
  return {
1012
1145
  name: "typescript",
@@ -1052,11 +1185,11 @@ const printerTs = ast.definePrinter((options) => {
1052
1185
  const hasStringLiteral = members.some((m) => {
1053
1186
  return ast.narrowSchema(m, ast.schemaTypes.enum)?.primitive === "string";
1054
1187
  });
1055
- const hasPlainString = members.some((m) => ast.isStringType(m));
1188
+ const hasPlainString = members.some((m) => isStringType(m));
1056
1189
  if (hasStringLiteral && hasPlainString) return createUnionDeclaration({
1057
1190
  withParentheses: true,
1058
1191
  nodes: members.map((m) => {
1059
- if (ast.isStringType(m)) return createIntersectionDeclaration({
1192
+ if (isStringType(m)) return createIntersectionDeclaration({
1060
1193
  nodes: [keywordTypeNodes.string, createTypeLiteralNode([])],
1061
1194
  withParentheses: true
1062
1195
  });
@@ -1076,7 +1209,7 @@ const printerTs = ast.definePrinter((options) => {
1076
1209
  },
1077
1210
  array(node) {
1078
1211
  return createArrayDeclaration({
1079
- nodes: (node.items ?? []).map((item) => this.transform(item)).filter(isNonNullable),
1212
+ nodes: mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(isNonNullable),
1080
1213
  arrayType: this.options.arrayType
1081
1214
  }) ?? null;
1082
1215
  },
@@ -1086,18 +1219,19 @@ const printerTs = ast.definePrinter((options) => {
1086
1219
  object(node) {
1087
1220
  const { transform, options } = this;
1088
1221
  const addsQuestionToken = OPTIONAL_ADDS_QUESTION_TOKEN.has(options.optionalType);
1089
- const propertyNodes = node.properties.map((prop) => {
1090
- const baseType = transform(prop.schema) ?? keywordTypeNodes.unknown;
1091
- const type = buildPropertyType(prop.schema, baseType, options.optionalType);
1092
- const propMeta = ast.syncSchemaRef(prop.schema);
1222
+ const propertyNodes = mapSchemaProperties(node, (schema) => transform(schema)).map(({ name, property, output }) => {
1223
+ const baseType = output ?? keywordTypeNodes.unknown;
1224
+ const optional = !property.required || !!property.schema.optional || !!property.schema.nullish;
1225
+ const type = buildPropertyType(property.schema, baseType, options.optionalType, optional);
1226
+ const propMeta = syncSchemaRef(property.schema);
1093
1227
  return appendJSDocToNode({
1094
1228
  node: createPropertySignature({
1095
- questionToken: prop.schema.optional || prop.schema.nullish ? addsQuestionToken : false,
1096
- name: prop.name,
1229
+ questionToken: optional ? addsQuestionToken : false,
1230
+ name,
1097
1231
  type,
1098
1232
  readOnly: propMeta?.readOnly
1099
1233
  }),
1100
- comments: buildPropertyJSDocComments(prop.schema)
1234
+ comments: buildPropertyJSDocComments(property.schema, optional)
1101
1235
  });
1102
1236
  });
1103
1237
  const allElements = [...propertyNodes, ...buildIndexSignatures(node, propertyNodes.length, transform)];
@@ -1110,7 +1244,7 @@ const printerTs = ast.definePrinter((options) => {
1110
1244
  const { name, syntaxType = "type", description, keysToOmit } = this.options;
1111
1245
  const transformed = this.transform(node);
1112
1246
  if (!transformed) return null;
1113
- const meta = ast.syncSchemaRef(node);
1247
+ const meta = syncSchemaRef(node);
1114
1248
  if (!name) {
1115
1249
  const withNullable = meta.nullable ? createUnionDeclaration({ nodes: [transformed, keywordTypeNodes.null] }) : transformed;
1116
1250
  const result = (meta.nullish || meta.optional) && addsUndefined ? createUnionDeclaration({ nodes: [withNullable, keywordTypeNodes.undefined] }) : withNullable;
@@ -1232,9 +1366,9 @@ const typeGenerator = defineGenerator({
1232
1366
  });
1233
1367
  },
1234
1368
  operation(node, ctx) {
1235
- const { enum: enumOptions, optionalType, arrayType, syntaxType, paramsCasing, group, output, printer } = ctx.options;
1369
+ const { enum: enumOptions, optionalType, arrayType, syntaxType, group, output, printer } = ctx.options;
1236
1370
  const { adapter, config, resolver, root } = ctx;
1237
- const params = ast.caseParams(node.parameters, paramsCasing);
1371
+ const params = caseParams(node.parameters, "camelcase");
1238
1372
  const meta = { file: resolver.resolveFile({
1239
1373
  name: node.operationId,
1240
1374
  extname: ".ts",
@@ -1298,9 +1432,9 @@ const typeGenerator = defineGenerator({
1298
1432
  */
1299
1433
  function buildContentTypeVariants(entries, baseName, decorate) {
1300
1434
  const variants = resolveContentTypeVariants(entries, baseName);
1301
- const unionSchema = ast.createSchema({
1435
+ const unionSchema = ast.factory.createSchema({
1302
1436
  type: "union",
1303
- members: variants.map((variant) => ast.createSchema({
1437
+ members: variants.map((variant) => ast.factory.createSchema({
1304
1438
  type: "ref",
1305
1439
  name: variant.name
1306
1440
  }))
@@ -1315,7 +1449,10 @@ const typeGenerator = defineGenerator({
1315
1449
  })] });
1316
1450
  }
1317
1451
  const paramTypes = params.map((param) => renderSchemaType({
1318
- schema: param.schema,
1452
+ schema: {
1453
+ ...param.schema,
1454
+ optional: !param.required
1455
+ },
1319
1456
  name: resolver.resolveParamName(node, param)
1320
1457
  }));
1321
1458
  const requestBodyContent = node.requestBody?.content ?? [];
@@ -1511,7 +1648,7 @@ const pluginTs = definePlugin((options) => {
1511
1648
  const { output = {
1512
1649
  path: "types",
1513
1650
  barrel: { type: "named" }
1514
- }, group, exclude = [], include, override = [], enum: enumOptions = {}, optionalType = "questionToken", arrayType = "array", syntaxType = "type", paramsCasing, printer, resolver: userResolver, transformer: userTransformer, generators: userGenerators = [] } = options;
1651
+ }, group, exclude = [], include, override = [], enum: enumOptions = {}, optionalType = "questionToken", arrayType = "array", syntaxType = "type", printer, resolver: userResolver, macros: userMacros } = options;
1515
1652
  const groupConfig = createGroupConfig(group);
1516
1653
  const resolvedEnum = {
1517
1654
  type: enumOptions.type ?? "asConst",
@@ -1533,52 +1670,237 @@ const pluginTs = definePlugin((options) => {
1533
1670
  arrayType,
1534
1671
  enum: resolvedEnum,
1535
1672
  syntaxType,
1536
- paramsCasing,
1537
1673
  printer
1538
1674
  });
1539
1675
  ctx.setResolver(userResolver ? {
1540
1676
  ...resolverTs,
1541
1677
  ...userResolver
1542
1678
  } : resolverTs);
1543
- if (userTransformer) ctx.setTransformer(userTransformer);
1679
+ if (userMacros?.length) ctx.setMacros(userMacros);
1544
1680
  ctx.addGenerator(typeGenerator);
1545
- for (const gen of userGenerators) ctx.addGenerator(gen);
1546
1681
  } }
1547
1682
  };
1548
1683
  });
1549
1684
  //#endregion
1685
+ //#region src/printers/functionParams.ts
1686
+ /**
1687
+ * Narrows a {@link TypeExpression} to a {@link TypeLiteralNode}.
1688
+ */
1689
+ function isTypeLiteral(type) {
1690
+ return typeof type !== "string" && type.kind === "TypeLiteral";
1691
+ }
1692
+ /**
1693
+ * Narrows a {@link TypeExpression} to an {@link IndexedAccessTypeNode}.
1694
+ */
1695
+ function isIndexedAccessType(type) {
1696
+ return typeof type !== "string" && type.kind === "IndexedAccessType";
1697
+ }
1698
+ /**
1699
+ * Creates a {@link TypeLiteralNode} representing an inline anonymous object type.
1700
+ *
1701
+ * @example
1702
+ * ```ts
1703
+ * createTypeLiteral({ members: [{ name: 'petId', type: 'string', optional: false }] })
1704
+ * // { petId: string }
1705
+ * ```
1706
+ */
1707
+ function createTypeLiteral(props) {
1708
+ return {
1709
+ kind: "TypeLiteral",
1710
+ ...props
1711
+ };
1712
+ }
1713
+ /**
1714
+ * Creates an {@link IndexedAccessTypeNode} representing a single field accessed from a named type.
1715
+ *
1716
+ * @example
1717
+ * ```ts
1718
+ * createIndexedAccessType({ target: 'DeletePetPathParams', key: 'petId' })
1719
+ * // DeletePetPathParams['petId']
1720
+ * ```
1721
+ */
1722
+ function createIndexedAccessType(props) {
1723
+ return {
1724
+ kind: "IndexedAccessType",
1725
+ ...props
1726
+ };
1727
+ }
1728
+ /**
1729
+ * Creates an {@link ObjectBindingPatternNode} for a destructured parameter binding.
1730
+ *
1731
+ * @example
1732
+ * ```ts
1733
+ * createObjectBindingPattern({ elements: [{ name: 'id' }, { name: 'name' }] })
1734
+ * // { id, name }
1735
+ * ```
1736
+ */
1737
+ function createObjectBindingPattern(props) {
1738
+ return {
1739
+ kind: "ObjectBindingPattern",
1740
+ ...props
1741
+ };
1742
+ }
1743
+ /**
1744
+ * Creates a {@link FunctionParameterNode}. `optional` defaults to `false`.
1745
+ * Passing `properties` builds a destructured group: an {@link ObjectBindingPatternNode} name
1746
+ * paired with a {@link TypeLiteralNode} type.
1747
+ *
1748
+ * @example Optional param
1749
+ * ```ts
1750
+ * createFunctionParameter({ name: 'params', type: 'QueryParams', optional: true })
1751
+ * // → params?: QueryParams
1752
+ * ```
1753
+ *
1754
+ * @example Destructured group
1755
+ * ```ts
1756
+ * createFunctionParameter({ properties: [{ name: 'id', type: 'string' }, { name: 'name', type: 'string', optional: true }], default: '{}' })
1757
+ * // → { id, name }: { id: string; name?: string } = {}
1758
+ * ```
1759
+ */
1760
+ function createFunctionParameter(input) {
1761
+ if ("properties" in input) return {
1762
+ kind: "FunctionParameter",
1763
+ name: createObjectBindingPattern({ elements: input.properties.map((p) => ({ name: p.name })) }),
1764
+ type: createTypeLiteral({ members: input.properties.map((p) => ({
1765
+ name: p.name,
1766
+ type: p.type,
1767
+ optional: p.optional ?? false
1768
+ })) }),
1769
+ optional: input.optional ?? false,
1770
+ ...input.default !== void 0 ? { default: input.default } : {}
1771
+ };
1772
+ return {
1773
+ kind: "FunctionParameter",
1774
+ optional: false,
1775
+ ...input
1776
+ };
1777
+ }
1778
+ /**
1779
+ * Creates a {@link FunctionParametersNode} from an ordered list of parameters.
1780
+ *
1781
+ * @example
1782
+ * ```ts
1783
+ * const empty = createFunctionParameters()
1784
+ * // { params: [] }
1785
+ * ```
1786
+ */
1787
+ function createFunctionParameters(props = {}) {
1788
+ return {
1789
+ kind: "FunctionParameters",
1790
+ params: [],
1791
+ ...props
1792
+ };
1793
+ }
1794
+ //#endregion
1550
1795
  //#region src/printers/functionPrinter.ts
1551
- const kindToHandlerKey = {
1552
- FunctionParameter: "functionParameter",
1553
- ParameterGroup: "parameterGroup",
1554
- FunctionParameters: "functionParameters",
1555
- ParamsType: "paramsType"
1556
- };
1557
1796
  /**
1558
- * Creates a function-parameter printer factory.
1797
+ * Renders a {@link TypeExpression} to its TypeScript source.
1798
+ *
1799
+ * - a `string` is a type reference, returned as-is
1800
+ * - an `IndexedAccessType` becomes `objectType['indexType']`
1801
+ * - a `TypeLiteral` becomes `{ key: Type; key?: Type }`
1559
1802
  *
1560
- * Uses `createPrinterFactory` and dispatches handlers by `node.kind`
1561
- * (for function nodes) rather than by `node.type` (for schema nodes).
1803
+ * `transformType` is applied once to the fully rendered type, matching how the
1804
+ * printer wrapped reference types before the `ts.factory` model.
1562
1805
  */
1563
- const defineFunctionPrinter = ast.createPrinterFactory((node) => kindToHandlerKey[node.kind]);
1806
+ function renderType(type, transformType) {
1807
+ const rendered = renderTypeExpression(type);
1808
+ return transformType ? transformType(rendered) : rendered;
1809
+ }
1810
+ /**
1811
+ * Renders an object-type key, quoting it as a string literal when it is not a valid
1812
+ * identifier so `{ 'content-type': string }` stays valid TypeScript.
1813
+ */
1814
+ function renderKey(name) {
1815
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
1816
+ }
1817
+ function renderTypeExpression(type) {
1818
+ if (typeof type === "string") return type;
1819
+ if (isIndexedAccessType(type)) return `${type.target}['${type.key}']`;
1820
+ return `{ ${type.members.map((member) => {
1821
+ const value = renderTypeExpression(member.type);
1822
+ return member.optional ? `${renderKey(member.name)}?: ${value}` : `${renderKey(member.name)}: ${value}`;
1823
+ }).join("; ")} }`;
1824
+ }
1825
+ function groupMembers(param) {
1826
+ if (typeof param.name === "string") return null;
1827
+ return param.type && isTypeLiteral(param.type) ? param.type.members : [];
1828
+ }
1564
1829
  function rank(param) {
1565
- if (param.kind === "ParameterGroup") {
1566
- if (param.default) return PARAM_RANK.withDefault;
1567
- return param.optional ?? param.properties.every((p) => p.optional || p.default !== void 0) ? PARAM_RANK.optional : PARAM_RANK.required;
1568
- }
1569
1830
  if (param.rest) return PARAM_RANK.rest;
1570
1831
  if (param.default) return PARAM_RANK.withDefault;
1832
+ const members = groupMembers(param);
1833
+ if (members) return members.every((m) => m.optional) ? PARAM_RANK.optional : PARAM_RANK.required;
1571
1834
  return param.optional ? PARAM_RANK.optional : PARAM_RANK.required;
1572
1835
  }
1573
1836
  function sortParams(params) {
1574
1837
  return params.toSorted((a, b) => rank(a) - rank(b));
1575
1838
  }
1576
- function sortChildParams(params) {
1577
- return params.toSorted((a, b) => rank(a) - rank(b));
1839
+ function sortedGroupMembers(name, type) {
1840
+ const members = type && isTypeLiteral(type) ? type.members : [];
1841
+ const memberRank = (optional) => optional ? PARAM_RANK.optional : PARAM_RANK.required;
1842
+ return name.elements.map((element, index) => ({
1843
+ name: element.name,
1844
+ propertyName: element.propertyName,
1845
+ type: members[index]?.type,
1846
+ optional: members[index]?.optional
1847
+ })).toSorted((a, b) => memberRank(a.optional) - memberRank(b.optional));
1848
+ }
1849
+ /**
1850
+ * Renders one binding element: the local name, prefixed with its source key when the
1851
+ * binding renames the property, as in `{ petId: id }`.
1852
+ */
1853
+ function renderBindingMember(member, transformName) {
1854
+ const local = transformName ? transformName(member.name) : member.name;
1855
+ return member.propertyName ? `${member.propertyName}: ${local}` : local;
1578
1856
  }
1579
1857
  /**
1580
- * Default function-signature printer.
1581
- * Covers the four standard output modes used across Kubb plugins.
1858
+ * Renders the type annotation of a destructured group. An inline object type is
1859
+ * rendered from the already-sorted members so its key order matches the binding;
1860
+ * a reference type is rendered as-is.
1861
+ */
1862
+ function renderGroupType(type, sorted, transformType) {
1863
+ if (!type) return void 0;
1864
+ if (!isTypeLiteral(type)) return renderType(type);
1865
+ const typed = sorted.filter((member) => member.type !== void 0);
1866
+ if (!typed.length) return void 0;
1867
+ return `{ ${typed.map((member) => {
1868
+ const key = renderKey(member.propertyName ?? member.name);
1869
+ const value = renderType(member.type, transformType);
1870
+ return member.optional ? `${key}?: ${value}` : `${key}: ${value}`;
1871
+ }).join("; ")} }`;
1872
+ }
1873
+ function printParameter(node, options) {
1874
+ const { mode, transformName, transformType } = options;
1875
+ const isGroup = typeof node.name !== "string";
1876
+ if (mode === "call") {
1877
+ if (isGroup) return `{ ${sortedGroupMembers(node.name, node.type).map((member) => renderBindingMember(member)).join(", ")} }`;
1878
+ const name = transformName ? transformName(node.name) : node.name;
1879
+ return node.rest ? `...${name}` : name;
1880
+ }
1881
+ if (isGroup) {
1882
+ const sorted = sortedGroupMembers(node.name, node.type);
1883
+ const binding = `{ ${sorted.map((member) => renderBindingMember(member, transformName)).join(", ")} }`;
1884
+ const allOptional = sorted.every((member) => member.optional);
1885
+ const type = renderGroupType(node.type, sorted, transformType);
1886
+ if (type) {
1887
+ if (allOptional) return `${binding}: ${type} = ${node.default ?? "{}"}`;
1888
+ return node.default ? `${binding}: ${type} = ${node.default}` : `${binding}: ${type}`;
1889
+ }
1890
+ return node.default ? `${binding} = ${node.default}` : binding;
1891
+ }
1892
+ const name = transformName ? transformName(node.name) : node.name;
1893
+ const type = node.type ? renderType(node.type, transformType) : void 0;
1894
+ if (node.rest) return type ? `...${name}: ${type}` : `...${name}`;
1895
+ if (type) {
1896
+ if (node.optional) return `${name}?: ${type}`;
1897
+ return node.default ? `${name}: ${type} = ${node.default}` : `${name}: ${type}`;
1898
+ }
1899
+ return node.default ? `${name} = ${node.default}` : name;
1900
+ }
1901
+ /**
1902
+ * Default function-signature printer. Renders a parameter list in one of two modes:
1903
+ * `declaration` for the function signature and `call` for the call arguments.
1582
1904
  *
1583
1905
  * @example
1584
1906
  * ```ts
@@ -1594,68 +1916,198 @@ function sortChildParams(params) {
1594
1916
  * printer.print(sig) // → "petId: string, config: Config = {}"
1595
1917
  * ```
1596
1918
  */
1597
- const functionPrinter = defineFunctionPrinter((options) => ({
1598
- name: "functionParameters",
1599
- options,
1600
- nodes: {
1601
- paramsType(node) {
1602
- if (node.kind !== "ParamsType") return null;
1603
- if (node.variant === "member") return `${node.base}['${node.key}']`;
1604
- if (node.variant === "struct") return `{ ${node.properties.map((p) => {
1605
- const typeStr = this.transform(p.type);
1606
- const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(p.name) ? p.name : JSON.stringify(p.name);
1607
- return p.optional ? `${key}?: ${typeStr}` : `${key}: ${typeStr}`;
1608
- }).join("; ")} }`;
1609
- if (node.variant === "reference") return node.name;
1610
- return null;
1611
- },
1612
- functionParameter(node) {
1613
- const { mode, transformName, transformType } = this.options;
1614
- const name = transformName ? transformName(node.name) : node.name;
1615
- const rawType = node.type ? this.transform(node.type) : void 0;
1616
- const type = rawType != null && transformType ? transformType(rawType) : rawType;
1617
- if (mode === "keys" || mode === "values") return node.rest ? `...${name}` : name;
1618
- if (mode === "call") return node.rest ? `...${name}` : name;
1619
- if (node.rest) return type ? `...${name}: ${type}` : `...${name}`;
1620
- if (type) {
1621
- if (node.optional) return `${name}?: ${type}`;
1622
- return node.default ? `${name}: ${type} = ${node.default}` : `${name}: ${type}`;
1623
- }
1624
- return node.default ? `${name} = ${node.default}` : name;
1625
- },
1626
- parameterGroup(node) {
1627
- const { mode, transformName, transformType } = this.options;
1628
- const sorted = sortChildParams(node.properties);
1629
- const isOptional = node.optional ?? sorted.every((p) => p.optional || p.default !== void 0);
1630
- if (node.inline) return sorted.map((p) => this.transform(p)).filter(Boolean).join(", ");
1631
- if (mode === "keys" || mode === "values") return `{ ${sorted.map((p) => p.name).join(", ")} }`;
1632
- if (mode === "call") return `{ ${sorted.map((p) => p.name).join(", ")} }`;
1633
- const names = sorted.map((p) => {
1634
- return transformName ? transformName(p.name) : p.name;
1635
- });
1636
- const nameStr = names.length ? `{ ${names.join(", ")} }` : void 0;
1637
- if (!nameStr) return null;
1638
- let typeAnnotation = node.type ? this.transform(node.type) ?? void 0 : void 0;
1639
- if (!typeAnnotation) {
1640
- const typeParts = sorted.filter((p) => p.type).map((p) => {
1641
- const rawT = p.type ? this.transform(p.type) : void 0;
1642
- const t = rawT != null && transformType ? transformType(rawT) : rawT;
1643
- return p.optional || p.default !== void 0 ? `${p.name}?: ${t}` : `${p.name}: ${t}`;
1644
- });
1645
- typeAnnotation = typeParts.length ? `{ ${typeParts.join("; ")} }` : void 0;
1646
- }
1647
- if (typeAnnotation) {
1648
- if (isOptional) return `${nameStr}: ${typeAnnotation} = ${node.default ?? "{}"}`;
1649
- return node.default ? `${nameStr}: ${typeAnnotation} = ${node.default}` : `${nameStr}: ${typeAnnotation}`;
1650
- }
1651
- return node.default ? `${nameStr} = ${node.default}` : nameStr;
1652
- },
1653
- functionParameters(node) {
1654
- return sortParams(node.params).map((p) => this.transform(p)).filter(Boolean).join(", ");
1919
+ function functionPrinter(options) {
1920
+ return {
1921
+ name: "functionParameters",
1922
+ options,
1923
+ print(node) {
1924
+ return sortParams(node.params).map((p) => printParameter(p, options)).filter(Boolean).join(", ");
1655
1925
  }
1926
+ };
1927
+ }
1928
+ //#endregion
1929
+ //#region src/printers/operationParams.ts
1930
+ /**
1931
+ * Resolves the {@link TypeExpression} for an individual parameter.
1932
+ *
1933
+ * Without a resolver, it falls back to the schema primitive (a plain type-name string). When the
1934
+ * parameter belongs to a named group, it emits an `IndexedAccessType` like `GroupParams['petId']`,
1935
+ * otherwise the resolved individual name.
1936
+ */
1937
+ function resolveParamType({ node, param, resolver }) {
1938
+ if (!resolver) return param.schema.primitive ?? "unknown";
1939
+ const individualName = resolver.resolveParamName(node, param);
1940
+ const groupLocation = param.in === "path" || param.in === "query" || param.in === "header" ? param.in : void 0;
1941
+ const groupResolvers = {
1942
+ path: resolver.resolvePathParamsName,
1943
+ query: resolver.resolveQueryParamsName,
1944
+ header: resolver.resolveHeaderParamsName
1945
+ };
1946
+ const groupName = groupLocation ? groupResolvers[groupLocation].call(resolver, node, param) : void 0;
1947
+ if (groupName && groupName !== individualName) return createIndexedAccessType({
1948
+ target: groupName,
1949
+ key: param.name
1950
+ });
1951
+ return individualName;
1952
+ }
1953
+ /**
1954
+ * Derives a {@link ParamGroupType} for a query or header group from the resolver.
1955
+ *
1956
+ * Returns `null` when there is no resolver, no params, or the group name equals the
1957
+ * individual param name (so there is no real group to emit).
1958
+ */
1959
+ function resolveGroupType({ node, params, group, resolver }) {
1960
+ if (!resolver || !params.length) return null;
1961
+ const firstParam = params[0];
1962
+ const groupName = (group === "query" ? resolver.resolveQueryParamsName : resolver.resolveHeaderParamsName).call(resolver, node, firstParam);
1963
+ if (groupName === resolver.resolveParamName(node, firstParam)) return null;
1964
+ return {
1965
+ type: groupName,
1966
+ optional: params.every((p) => !p.required)
1967
+ };
1968
+ }
1969
+ /**
1970
+ * Converts an `OperationNode` into function parameters for code generation.
1971
+ *
1972
+ * Centralizes parameter grouping logic for all plugins. `paramsType` chooses between one
1973
+ * destructured object parameter (`object`) and separate top-level parameters (`inline`), while
1974
+ * `pathParamsType` controls how path params render in inline mode. Provide a `resolver` for type
1975
+ * name resolution and `extraParams` for plugin-specific trailing parameters such as an `options` object.
1976
+ */
1977
+ function createOperationParams(node, options) {
1978
+ const { paramsType, pathParamsType, paramsCasing, resolver, pathParamsDefault, extraParams = [], paramNames, typeWrapper } = options;
1979
+ const dataName = paramNames?.data ?? "data";
1980
+ const paramsName = paramNames?.params ?? "params";
1981
+ const headersName = paramNames?.headers ?? "headers";
1982
+ const pathName = paramNames?.path ?? "pathParams";
1983
+ const wrapType = (type) => typeWrapper ? typeWrapper(type) : type;
1984
+ const wrapTypeExpression = (type) => typeof type === "string" ? wrapType(type) : type;
1985
+ const casedParams = caseParams(node.parameters, paramsCasing);
1986
+ const pathParams = casedParams.filter((p) => p.in === "path");
1987
+ const queryParams = casedParams.filter((p) => p.in === "query");
1988
+ const headerParams = casedParams.filter((p) => p.in === "header");
1989
+ const toProperty = (param) => ({
1990
+ name: param.name,
1991
+ type: wrapTypeExpression(resolveParamType({
1992
+ node,
1993
+ param,
1994
+ resolver
1995
+ })),
1996
+ optional: !param.required
1997
+ });
1998
+ const emptyObjectDefault = (props) => props.every((p) => p.optional) ? "{}" : void 0;
1999
+ const bodyType = node.requestBody?.content?.[0]?.schema ? wrapType(resolver?.resolveDataName(node) ?? "unknown") : void 0;
2000
+ const bodyProperty = bodyType ? [{
2001
+ name: dataName,
2002
+ type: bodyType,
2003
+ optional: !(node.requestBody?.required ?? false)
2004
+ }] : [];
2005
+ const trailingGroups = [{
2006
+ name: paramsName,
2007
+ node,
2008
+ params: queryParams,
2009
+ groupType: resolveGroupType({
2010
+ node,
2011
+ params: queryParams,
2012
+ group: "query",
2013
+ resolver
2014
+ }),
2015
+ resolver,
2016
+ wrapType
2017
+ }, {
2018
+ name: headersName,
2019
+ node,
2020
+ params: headerParams,
2021
+ groupType: resolveGroupType({
2022
+ node,
2023
+ params: headerParams,
2024
+ group: "header",
2025
+ resolver
2026
+ }),
2027
+ resolver,
2028
+ wrapType
2029
+ }];
2030
+ const params = [];
2031
+ if (paramsType === "object") {
2032
+ const children = [
2033
+ ...pathParams.map(toProperty),
2034
+ ...bodyProperty,
2035
+ ...trailingGroups.flatMap(buildGroupProperty)
2036
+ ];
2037
+ if (children.length) params.push(createFunctionParameter({
2038
+ properties: children,
2039
+ default: emptyObjectDefault(children)
2040
+ }));
2041
+ } else {
2042
+ if (pathParamsType === "inlineSpread" && pathParams.length) {
2043
+ const spreadType = resolver?.resolvePathParamsName(node, pathParams[0]);
2044
+ params.push(createFunctionParameter({
2045
+ name: pathName,
2046
+ type: spreadType ? wrapType(spreadType) : void 0,
2047
+ rest: true
2048
+ }));
2049
+ } else if (pathParamsType === "inline") params.push(...pathParams.map((p) => createFunctionParameter(toProperty(p))));
2050
+ else if (pathParams.length) {
2051
+ const pathChildren = pathParams.map(toProperty);
2052
+ params.push(createFunctionParameter({
2053
+ properties: pathChildren,
2054
+ default: pathParamsDefault ?? emptyObjectDefault(pathChildren)
2055
+ }));
2056
+ }
2057
+ params.push(...bodyProperty.map((p) => createFunctionParameter(p)));
2058
+ params.push(...trailingGroups.flatMap(buildGroupParam));
1656
2059
  }
1657
- }));
2060
+ params.push(...extraParams);
2061
+ return createFunctionParameters({ params });
2062
+ }
2063
+ /**
2064
+ * Builds the property descriptor for a query or header group.
2065
+ * Returns an empty array when there are no params to emit.
2066
+ *
2067
+ * A pre-resolved `groupType` emits `name: GroupType`. Otherwise it builds an inline
2068
+ * {@link TypeLiteralNode} from the individual params.
2069
+ */
2070
+ function buildGroupProperty({ name, node, params, groupType, resolver, wrapType }) {
2071
+ if (groupType) return [{
2072
+ name,
2073
+ type: typeof groupType.type === "string" ? wrapType(groupType.type) : groupType.type,
2074
+ optional: groupType.optional
2075
+ }];
2076
+ if (params.length) return [{
2077
+ name,
2078
+ type: buildTypeLiteral({
2079
+ node,
2080
+ params,
2081
+ resolver
2082
+ }),
2083
+ optional: params.every((p) => !p.required)
2084
+ }];
2085
+ return [];
2086
+ }
2087
+ /**
2088
+ * Builds a single {@link FunctionParameterNode} for a query or header group.
2089
+ * Returns an empty array when there are no params to emit.
2090
+ */
2091
+ function buildGroupParam(args) {
2092
+ return buildGroupProperty(args).map((p) => createFunctionParameter(p));
2093
+ }
2094
+ /**
2095
+ * Builds a {@link TypeLiteralNode} for an inline anonymous type grouping named fields.
2096
+ *
2097
+ * Used when query or header parameters have no dedicated group type name.
2098
+ */
2099
+ function buildTypeLiteral({ node, params, resolver }) {
2100
+ return createTypeLiteral({ members: params.map((p) => ({
2101
+ name: p.name,
2102
+ type: resolveParamType({
2103
+ node,
2104
+ param: p,
2105
+ resolver
2106
+ }),
2107
+ optional: !p.required
2108
+ })) });
2109
+ }
1658
2110
  //#endregion
1659
- export { Enum, Type, pluginTs as default, pluginTs, functionPrinter, pluginTsName, printerTs, resolverTs, typeGenerator };
2111
+ export { Enum, Type, createFunctionParameter, createFunctionParameters, createIndexedAccessType, createObjectBindingPattern, createOperationParams, createTypeLiteral, pluginTs as default, pluginTs, functionPrinter, pluginTsName, printerTs, renderType, resolverTs, typeGenerator };
1660
2112
 
1661
2113
  //# sourceMappingURL=index.js.map