@kubb/plugin-ts 5.0.0-beta.42 → 5.0.0-beta.64

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
@@ -28,12 +28,13 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  enumerable: true
29
29
  }) : target, mod));
30
30
  //#endregion
31
+ let _kubb_ast_utils = require("@kubb/ast/utils");
31
32
  let _kubb_parser_ts = require("@kubb/parser-ts");
32
33
  let _kubb_renderer_jsx = require("@kubb/renderer-jsx");
33
- let _kubb_core = require("@kubb/core");
34
34
  let typescript = require("typescript");
35
35
  typescript = __toESM(typescript, 1);
36
36
  let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
37
+ let _kubb_core = require("@kubb/core");
37
38
  //#region ../../internals/utils/src/casing.ts
38
39
  /**
39
40
  * Shared implementation for camelCase and PascalCase conversion.
@@ -45,58 +46,41 @@ let _kubb_renderer_jsx_jsx_runtime = require("@kubb/renderer-jsx/jsx-runtime");
45
46
  function toCamelOrPascal(text, pascal) {
46
47
  return text.trim().replace(/([a-z\d])([A-Z])/g, "$1 $2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2").replace(/(\d)([a-z])/g, "$1 $2").split(/[\s\-_./\\:]+/).filter(Boolean).map((word, i) => {
47
48
  if (word.length > 1 && word === word.toUpperCase()) return word;
48
- if (i === 0 && !pascal) return word.charAt(0).toLowerCase() + word.slice(1);
49
- return word.charAt(0).toUpperCase() + word.slice(1);
49
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
50
50
  }).join("").replace(/[^a-zA-Z0-9]/g, "");
51
51
  }
52
52
  /**
53
- * Splits `text` on `.` and applies `transformPart` to each segment.
54
- * The last segment receives `isLast = true`, all earlier segments receive `false`.
55
- * Segments are joined with `/` to form a file path.
56
- *
57
- * Only splits on dots followed by a letter so that version numbers
58
- * embedded in operationIds (e.g. `v2025.0`) are kept intact.
59
- */
60
- function applyToFileParts(text, transformPart) {
61
- const parts = text.split(/\.(?=[a-zA-Z])/);
62
- return parts.map((part, i) => transformPart(part, i === parts.length - 1)).join("/");
63
- }
64
- /**
65
53
  * Converts `text` to camelCase.
66
- * When `isFile` is `true`, dot-separated segments are each cased independently and joined with `/`.
67
54
  *
68
- * @example
69
- * camelCase('hello-world') // 'helloWorld'
70
- * camelCase('pet.petId', { isFile: true }) // 'pet/petId'
55
+ * @example Word boundaries
56
+ * `camelCase('hello-world') // 'helloWorld'`
57
+ *
58
+ * @example With a prefix
59
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
71
60
  */
72
- function camelCase(text, { isFile, prefix = "", suffix = "" } = {}) {
73
- if (isFile) return applyToFileParts(text, (part, isLast) => camelCase(part, isLast ? {
74
- prefix,
75
- suffix
76
- } : {}));
61
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
77
62
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
78
63
  }
79
64
  /**
80
65
  * Converts `text` to PascalCase.
81
- * When `isFile` is `true`, the last dot-separated segment is PascalCased and earlier segments are camelCased.
82
66
  *
83
- * @example
84
- * pascalCase('hello-world') // 'HelloWorld'
85
- * pascalCase('pet.petId', { isFile: true }) // 'pet/PetId'
67
+ * @example Word boundaries
68
+ * `pascalCase('hello-world') // 'HelloWorld'`
69
+ *
70
+ * @example With a suffix
71
+ * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
86
72
  */
87
- function pascalCase(text, { isFile, prefix = "", suffix = "" } = {}) {
88
- if (isFile) return applyToFileParts(text, (part, isLast) => isLast ? pascalCase(part, {
89
- prefix,
90
- suffix
91
- }) : camelCase(part));
73
+ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
92
74
  return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
93
75
  }
94
76
  /**
95
77
  * Converts `text` to snake_case.
96
78
  *
97
- * @example
98
- * snakeCase('helloWorld') // 'hello_world'
99
- * snakeCase('Hello-World') // 'hello_world'
79
+ * @example From camelCase
80
+ * `snakeCase('helloWorld') // 'hello_world'`
81
+ *
82
+ * @example From mixed separators
83
+ * `snakeCase('Hello-World') // 'hello_world'`
100
84
  */
101
85
  function snakeCase(text, { prefix = "", suffix = "" } = {}) {
102
86
  return `${prefix} ${text} ${suffix}`.trim().replace(/([a-z])([A-Z])/g, "$1_$2").replace(/[\s\-.]+/g, "_").replace(/[^a-zA-Z0-9_]/g, "").toLowerCase().split("_").filter(Boolean).join("_");
@@ -104,8 +88,8 @@ function snakeCase(text, { prefix = "", suffix = "" } = {}) {
104
88
  /**
105
89
  * Converts `text` to SCREAMING_SNAKE_CASE.
106
90
  *
107
- * @example
108
- * screamingSnakeCase('helloWorld') // 'HELLO_WORLD'
91
+ * @example From camelCase
92
+ * `screamingSnakeCase('helloWorld') // 'HELLO_WORLD'`
109
93
  */
110
94
  function screamingSnakeCase(text, { prefix = "", suffix = "" } = {}) {
111
95
  return snakeCase(text, {
@@ -114,60 +98,29 @@ function screamingSnakeCase(text, { prefix = "", suffix = "" } = {}) {
114
98
  }).toUpperCase();
115
99
  }
116
100
  //#endregion
117
- //#region ../../internals/utils/src/string.ts
101
+ //#region ../../internals/utils/src/fs.ts
118
102
  /**
119
- * Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
120
- * Returns the string unchanged when no balanced quote pair is found.
103
+ * Builds a nested file path from a dotted name. Splits on dots that precede a letter
104
+ * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
105
+ * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
121
106
  *
122
- * @example
123
- * trimQuotes('"hello"') // 'hello'
124
- * trimQuotes('hello') // 'hello'
125
- */
126
- function trimQuotes(text) {
127
- if (text.length >= 2) {
128
- const first = text[0];
129
- const last = text[text.length - 1];
130
- if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
131
- }
132
- return text;
133
- }
134
- /**
135
- * Escapes characters that are not allowed inside JS string literals.
136
- * Handles quotes, backslashes, and Unicode line terminators (U+2028 / U+2029).
107
+ * Empty segments are dropped before joining. They arise when the name starts with a dot
108
+ * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
109
+ * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
110
+ * absolute path, letting generated files escape the configured output directory.
137
111
  *
138
- * @see http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
112
+ * @example Nested path from a dotted name
113
+ * `toFilePath('pet.petId') // 'pet/petId'`
139
114
  *
140
- * @example
141
- * ```ts
142
- * jsStringEscape('say "hi"\nbye') // 'say \\"hi\\"\\nbye'
143
- * ```
144
- */
145
- function jsStringEscape(input) {
146
- return `${input}`.replace(/["'\\\n\r\u2028\u2029]/g, (character) => {
147
- switch (character) {
148
- case "\"":
149
- case "'":
150
- case "\\": return `\\${character}`;
151
- case "\n": return "\\n";
152
- case "\r": return "\\r";
153
- case "\u2028": return "\\u2028";
154
- case "\u2029": return "\\u2029";
155
- default: return "";
156
- }
157
- });
158
- }
159
- //#endregion
160
- //#region ../../internals/utils/src/object.ts
161
- /**
162
- * Serializes a primitive value to a JSON string literal, stripping any surrounding quote characters first.
115
+ * @example PascalCase the final segment
116
+ * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
163
117
  *
164
- * @example
165
- * stringify('hello') // '"hello"'
166
- * stringify('"hello"') // '"hello"'
118
+ * @example Suffix applied to the final segment only
119
+ * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
167
120
  */
168
- function stringify(value) {
169
- if (value === void 0 || value === null) return "\"\"";
170
- return JSON.stringify(trimQuotes(value.toString()));
121
+ function toFilePath(name, caseLast = camelCase) {
122
+ const parts = name.split(/\.(?=[a-zA-Z])/);
123
+ return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
171
124
  }
172
125
  //#endregion
173
126
  //#region ../../internals/utils/src/reserved.ts
@@ -303,26 +256,24 @@ const OPTIONAL_ADDS_UNDEFINED = new Set(["undefined", "questionTokenAndUndefined
303
256
  */
304
257
  const OPTIONAL_ADDS_QUESTION_TOKEN = new Set(["questionToken", "questionTokenAndUndefined"]);
305
258
  /**
306
- * `enumType` values that append a `Key` suffix to the generated enum type alias.
259
+ * `enum.type` values that append a `typeSuffix` to the generated enum type alias.
307
260
  */
308
- const ENUM_TYPES_WITH_KEY_SUFFIX = new Set(["asConst", "asPascalConst"]);
261
+ const ENUM_TYPES_WITH_KEY_SUFFIX = new Set(["asConst"]);
309
262
  /**
310
- * `enumType` values that require a runtime value declaration (object, enum, or literal).
263
+ * `enum.type` values that require a runtime value declaration (object, enum, or literal).
311
264
  */
312
265
  const ENUM_TYPES_WITH_RUNTIME_VALUE = new Set([
313
266
  "enum",
314
267
  "asConst",
315
- "asPascalConst",
316
268
  "constEnum",
317
269
  "literal",
318
270
  void 0
319
271
  ]);
320
272
  /**
321
- * `enumType` values whose type declaration is type-only (no runtime value emitted for the type alias).
273
+ * `enum.type` values whose type declaration is type-only (no runtime value emitted for the type alias).
322
274
  */
323
275
  const ENUM_TYPES_WITH_TYPE_ONLY = new Set([
324
276
  "asConst",
325
- "asPascalConst",
326
277
  "literal",
327
278
  void 0
328
279
  ]);
@@ -533,7 +484,7 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
533
484
  return factory.createLiteralTypeNode(factory.createNumericLiteral(value?.toString()));
534
485
  }
535
486
  if (typeof value === "boolean") return factory.createLiteralTypeNode(value ? factory.createTrue() : factory.createFalse());
536
- if (value) return factory.createLiteralTypeNode(factory.createStringLiteral(value.toString()));
487
+ if (value !== null && value !== void 0) return factory.createLiteralTypeNode(factory.createStringLiteral(value.toString()));
537
488
  }).filter((node) => node !== void 0)))];
538
489
  if (type === "enum" || type === "constEnum") return [void 0, factory.createEnumDeclaration([factory.createToken(typescript.default.SyntaxKind.ExportKeyword), type === "constEnum" ? factory.createToken(typescript.default.SyntaxKind.ConstKeyword) : void 0].filter((modifier) => modifier !== void 0), factory.createIdentifier(typeName), enums.map(([key, value]) => {
539
490
  let initializer = factory.createStringLiteral(value?.toString());
@@ -544,7 +495,7 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
544
495
  const casingKey = applyEnumKeyCasing(`${typeName}_${key}`, enumKeyCasing);
545
496
  return factory.createEnumMember(propertyName(casingKey), initializer);
546
497
  }
547
- if (key) {
498
+ if (key !== null && key !== void 0) {
548
499
  const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
549
500
  return factory.createEnumMember(propertyName(casingKey), initializer);
550
501
  }
@@ -556,7 +507,7 @@ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCa
556
507
  if (isNumber(value)) if (value < 0) initializer = factory.createPrefixUnaryExpression(typescript.default.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)));
557
508
  else initializer = factory.createNumericLiteral(value);
558
509
  if (typeof value === "boolean") initializer = value ? factory.createTrue() : factory.createFalse();
559
- if (key) {
510
+ if (key !== null && key !== void 0) {
560
511
  const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
561
512
  return factory.createPropertyAssignment(propertyName(casingKey), initializer);
562
513
  }
@@ -642,12 +593,10 @@ const createStringLiteral = factory.createStringLiteral;
642
593
  * Creates an array type node (e.g., `T[]`).
643
594
  */
644
595
  const createArrayTypeNode = factory.createArrayTypeNode;
645
- factory.createParenthesizedType;
646
596
  /**
647
597
  * Creates a literal type node (e.g., `'hello'`, `42`, `true`).
648
598
  */
649
599
  const createLiteralTypeNode = factory.createLiteralTypeNode;
650
- factory.createNull;
651
600
  /**
652
601
  * Creates an identifier node.
653
602
  */
@@ -672,8 +621,6 @@ const createTrue = factory.createTrue;
672
621
  * Creates a boolean false literal type node.
673
622
  */
674
623
  const createFalse = factory.createFalse;
675
- factory.createIndexedAccessTypeNode;
676
- factory.createTypeOperatorNode;
677
624
  /**
678
625
  * Creates a prefix unary expression (e.g., negative numbers, logical not).
679
626
  */
@@ -721,12 +668,12 @@ function buildTupleNode(node, print) {
721
668
  /**
722
669
  * Applies `nullable` and optional/nullish `| undefined` union modifiers to a property's resolved base type.
723
670
  */
724
- function buildPropertyType(schema, baseType, optionalType) {
671
+ function buildPropertyType(schema, baseType, optionalType, optional) {
725
672
  const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(optionalType);
726
- const meta = _kubb_core.ast.syncSchemaRef(schema);
673
+ const meta = (0, _kubb_ast_utils.syncSchemaRef)(schema);
727
674
  let type = baseType;
728
675
  if (meta.nullable) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.null] });
729
- if ((meta.nullish || meta.optional) && addsUndefined) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.undefined] });
676
+ if ((optional || meta.nullish || meta.optional) && addsUndefined) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.undefined] });
730
677
  return type;
731
678
  }
732
679
  /**
@@ -754,41 +701,44 @@ function buildIndexSignatures(node, propertyCount, print) {
754
701
  * Resolves the runtime identifier name and the TypeScript type name for an enum schema node.
755
702
  *
756
703
  * The raw `node.name` may be a YAML key such as `"enumNames.Type"` which is not a
757
- * valid TypeScript identifier. The resolver normalizes it; for inline enum
758
- * properties the adapter already emits a PascalCase+suffix name so resolution is typically a no-op.
704
+ * valid TypeScript identifier. The resolver normalizes it. For inline enum properties the adapter
705
+ * already emits a PascalCase+suffix name, so resolution is typically a no-op.
706
+ *
707
+ * When `constCasing` is `'pascalCase'` and `typeSuffix` is empty, the const and the type
708
+ * resolve to the same name, which TypeScript merges into a single value+type declaration.
759
709
  */
760
- function getEnumNames({ node, enumType, enumTypeSuffix, resolver }) {
710
+ function getEnumNames({ node, enum: enumOptions, resolver }) {
761
711
  const resolved = resolver.default(node.name, "type");
762
712
  return {
763
- enumName: enumType === "asPascalConst" ? resolved : camelCase(node.name),
764
- typeName: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) ? resolver.resolveEnumKeyName(node, enumTypeSuffix) : resolved
713
+ enumName: enumOptions.constCasing === "pascalCase" ? resolved : camelCase(node.name),
714
+ typeName: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) ? resolver.resolveEnumKeyName(node, enumOptions.typeSuffix) : resolved
765
715
  };
766
716
  }
767
717
  /**
768
718
  * Renders the enum declaration(s) for a single named `EnumSchemaNode`.
769
719
  *
770
- * Depending on `enumType` this may emit:
771
- * - A runtime object (`asConst` / `asPascalConst`) plus a `typeof` type alias
720
+ * Depending on `enum.type` this may emit:
721
+ * - A runtime object (`asConst`) plus a `typeof` type alias
772
722
  * - A `const enum` or plain `enum` declaration (`constEnum` / `enum`)
773
723
  * - A union literal type alias (`literal`)
774
724
  *
775
725
  * The emitted `File.Source` nodes carry the resolved names so that the barrel
776
726
  * index picks up the correct export identifiers.
777
727
  */
778
- function Enum({ node, enumType, enumTypeSuffix, enumKeyCasing, resolver }) {
728
+ function Enum({ node, enum: enumOptions, resolver }) {
779
729
  const { enumName, typeName } = getEnumNames({
780
730
  node,
781
- enumType,
782
- enumTypeSuffix,
731
+ enum: enumOptions,
783
732
  resolver
784
733
  });
785
734
  const [nameNode, typeNode] = createEnumDeclaration({
786
735
  name: enumName,
787
736
  typeName,
788
- enums: node.namedEnumValues?.map((v) => [trimQuotes(v.name.toString()), v.value]) ?? node.enumValues?.filter((v) => v !== null && v !== void 0).map((v) => [trimQuotes(v.toString()), v]) ?? [],
789
- type: enumType,
790
- enumKeyCasing
737
+ enums: node.namedEnumValues?.map((v) => [(0, _kubb_ast_utils.trimQuotes)(v.name.toString()), v.value]) ?? node.enumValues?.filter((v) => v !== null && v !== void 0).map((v) => [(0, _kubb_ast_utils.trimQuotes)(v.toString()), v]) ?? [],
738
+ type: enumOptions.type,
739
+ enumKeyCasing: enumOptions.keyCasing
791
740
  });
741
+ const namesMerge = !!nameNode && enumName === typeName;
792
742
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [nameNode && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
793
743
  name: enumName,
794
744
  isExportable: true,
@@ -797,15 +747,15 @@ function Enum({ node, enumType, enumTypeSuffix, enumKeyCasing, resolver }) {
797
747
  children: _kubb_parser_ts.parserTs.print(nameNode)
798
748
  }), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
799
749
  name: typeName,
800
- isIndexable: true,
801
- isExportable: ENUM_TYPES_WITH_RUNTIME_VALUE.has(enumType),
802
- isTypeOnly: ENUM_TYPES_WITH_TYPE_ONLY.has(enumType),
750
+ isIndexable: !namesMerge,
751
+ isExportable: !namesMerge && ENUM_TYPES_WITH_RUNTIME_VALUE.has(enumOptions.type),
752
+ isTypeOnly: ENUM_TYPES_WITH_TYPE_ONLY.has(enumOptions.type),
803
753
  children: _kubb_parser_ts.parserTs.print(typeNode)
804
754
  })] });
805
755
  }
806
756
  //#endregion
807
757
  //#region src/components/Type.tsx
808
- function Type({ name, node, printer, enumType, enumTypeSuffix, enumKeyCasing, resolver }) {
758
+ function Type({ name, node, printer, enum: enumOptions, resolver }) {
809
759
  const enumSchemaNodes = _kubb_core.ast.collect(node, { schema(n) {
810
760
  const enumNode = _kubb_core.ast.narrowSchema(n, _kubb_core.ast.schemaTypes.enum);
811
761
  if (enumNode?.name) return enumNode;
@@ -817,19 +767,16 @@ function Type({ name, node, printer, enumType, enumTypeSuffix, enumKeyCasing, re
817
767
  node,
818
768
  ...getEnumNames({
819
769
  node,
820
- enumType,
821
- enumTypeSuffix,
770
+ enum: enumOptions,
822
771
  resolver
823
772
  })
824
773
  };
825
774
  });
826
- const shouldExportEnums = enumType !== "inlineLiteral";
827
- const shouldExportType = enumType === "inlineLiteral" || enums.every((item) => item.typeName !== name);
775
+ const shouldExportEnums = enumOptions.type !== "inlineLiteral";
776
+ const shouldExportType = enumOptions.type === "inlineLiteral" || enums.every((item) => item.typeName !== name);
828
777
  return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [shouldExportEnums && enums.map(({ node }) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Enum, {
829
778
  node,
830
- enumType,
831
- enumTypeSuffix,
832
- enumKeyCasing,
779
+ enum: enumOptions,
833
780
  resolver
834
781
  }, node.name)), shouldExportType && /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Source, {
835
782
  name,
@@ -890,7 +837,7 @@ function resolveContentTypeVariants(entries, baseName) {
890
837
  });
891
838
  }
892
839
  function getOperationParameters(node, options = {}) {
893
- const params = _kubb_core.ast.caseParams(node.parameters, options.paramsCasing);
840
+ const params = (0, _kubb_ast_utils.caseParams)(node.parameters, options.paramsCasing);
894
841
  return {
895
842
  path: params.filter((param) => param.in === "path"),
896
843
  query: params.filter((param) => param.in === "query"),
@@ -905,26 +852,24 @@ function getOperationParameters(node, options = {}) {
905
852
  * shared default naming so every plugin groups output consistently:
906
853
  *
907
854
  * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
908
- * - other groups use `${camelCase(group)}${suffix}` (e.g. `petController`).
855
+ * - other groups use the camelCased group (`pet store` → `petStore`).
909
856
  *
910
857
  * A user-provided `group.name` always wins over the default namer, so callers stay in
911
858
  * control of their output folders. Returns `null` when grouping is disabled, matching the
912
859
  * per-plugin convention.
913
860
  *
914
861
  * @param group - The user-supplied group option, or `undefined` to disable grouping.
915
- * @param options.suffix - Appended to non-`path` group names, e.g. `'Controller'` or `'Requests'`.
916
862
  *
917
863
  * @example
918
864
  * ```ts
919
- * createGroupConfig(group, { suffix: 'Controller' }) // plugin-ts, plugin-client, …
920
- * createGroupConfig(group, { suffix: 'Requests' }) // plugin-cypress, plugin-mcp
865
+ * createGroupConfig(group) // shared across every plugin
921
866
  * ```
922
867
  */
923
- function createGroupConfig(group, options) {
868
+ function createGroupConfig(group) {
924
869
  if (!group) return null;
925
870
  const defaultName = (ctx) => {
926
871
  if (group.type === "path") return `${ctx.group.split("/")[1]}`;
927
- return `${camelCase(ctx.group)}${options.suffix}`;
872
+ return camelCase(ctx.group);
928
873
  };
929
874
  return {
930
875
  ...group,
@@ -940,97 +885,102 @@ function createGroupConfig(group, options) {
940
885
  * Constraint metadata (min/max length, pattern, multipleOf, min/maxProperties) is emitted as plain-text lines.
941
886
 
942
887
  */
943
- function buildPropertyJSDocComments(schema) {
944
- const meta = _kubb_core.ast.syncSchemaRef(schema);
888
+ function isSchemaOptional(schema) {
889
+ return Boolean("optional" in schema && schema.optional || "nullish" in schema && schema.nullish);
890
+ }
891
+ function buildPropertyJSDocComments(schema, optional) {
892
+ const meta = (0, _kubb_ast_utils.syncSchemaRef)(schema);
945
893
  const isArray = meta?.primitive === "array";
946
894
  const hasDescription = meta && "description" in meta && meta.description;
947
895
  const formatComment = meta && "format" in meta && meta.format ? hasDescription ? [" ", `Format: \`${meta.format}\``] : ["@description", `Format: \`${meta.format}\``] : [];
948
896
  return [
949
- hasDescription ? `@description ${jsStringEscape(meta.description)}` : null,
897
+ hasDescription ? `@description ${(0, _kubb_ast_utils.jsStringEscape)(meta.description)}` : null,
950
898
  ...formatComment,
951
899
  meta && "deprecated" in meta && meta.deprecated ? "@deprecated" : null,
952
900
  !isArray && meta && "min" in meta && meta.min !== void 0 ? `@minLength ${meta.min}` : null,
953
901
  !isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
954
902
  meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
955
- meta && "default" in meta && meta.default !== void 0 ? `@default ${"primitive" in meta && meta.primitive === "string" ? stringify(meta.default) : meta.default}` : null,
903
+ meta && "default" in meta && meta.default !== void 0 ? `@default ${"primitive" in meta && meta.primitive === "string" ? (0, _kubb_ast_utils.stringify)(meta.default) : meta.default}` : null,
956
904
  meta && "example" in meta && meta.example !== void 0 ? `@example ${meta.example}` : null,
957
- meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, "optional" in schema && schema.optional ? " | undefined" : null].filter(Boolean).join("") : null
905
+ meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, optional ?? isSchemaOptional(schema) ? " | undefined" : null].filter(Boolean).join("") : null
958
906
  ].filter(Boolean);
959
907
  }
960
908
  function buildParams(node, { params, resolver }) {
961
- return _kubb_core.ast.createSchema({
909
+ return _kubb_core.ast.factory.createSchema({
962
910
  type: "object",
963
- properties: params.map((param) => _kubb_core.ast.createProperty({
911
+ properties: params.map((param) => _kubb_core.ast.factory.createProperty({
964
912
  name: param.name,
965
913
  required: param.required,
966
- schema: _kubb_core.ast.createSchema({
914
+ schema: _kubb_core.ast.factory.createSchema({
967
915
  type: "ref",
968
- name: resolver.resolveParamName(node, param)
916
+ name: resolver.resolveParamName(node, param),
917
+ optional: !param.required
969
918
  })
970
919
  }))
971
920
  });
972
921
  }
973
922
  function buildData(node, { resolver }) {
974
923
  const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
975
- return _kubb_core.ast.createSchema({
924
+ return _kubb_core.ast.factory.createSchema({
976
925
  type: "object",
977
926
  deprecated: node.deprecated,
978
927
  properties: [
979
- _kubb_core.ast.createProperty({
928
+ _kubb_core.ast.factory.createProperty({
980
929
  name: "data",
981
- schema: node.requestBody?.content?.[0]?.schema ? _kubb_core.ast.createSchema({
930
+ schema: node.requestBody?.content?.[0]?.schema ? _kubb_core.ast.factory.createSchema({
982
931
  type: "ref",
983
932
  name: resolver.resolveDataName(node),
984
933
  optional: true
985
- }) : _kubb_core.ast.createSchema({
934
+ }) : _kubb_core.ast.factory.createSchema({
986
935
  type: "never",
987
936
  primitive: void 0,
988
937
  optional: true
989
938
  })
990
939
  }),
991
- _kubb_core.ast.createProperty({
940
+ _kubb_core.ast.factory.createProperty({
992
941
  name: "pathParams",
993
942
  required: pathParams.length > 0,
994
943
  schema: pathParams.length > 0 ? buildParams(node, {
995
944
  params: pathParams,
996
945
  resolver
997
- }) : _kubb_core.ast.createSchema({
946
+ }) : _kubb_core.ast.factory.createSchema({
998
947
  type: "never",
999
- primitive: void 0
948
+ primitive: void 0,
949
+ optional: true
1000
950
  })
1001
951
  }),
1002
- _kubb_core.ast.createProperty({
952
+ _kubb_core.ast.factory.createProperty({
1003
953
  name: "queryParams",
1004
- schema: queryParams.length > 0 ? _kubb_core.ast.createSchema({
954
+ schema: queryParams.length > 0 ? _kubb_core.ast.factory.createSchema({
1005
955
  ...buildParams(node, {
1006
956
  params: queryParams,
1007
957
  resolver
1008
958
  }),
1009
959
  optional: true
1010
- }) : _kubb_core.ast.createSchema({
960
+ }) : _kubb_core.ast.factory.createSchema({
1011
961
  type: "never",
1012
962
  primitive: void 0,
1013
963
  optional: true
1014
964
  })
1015
965
  }),
1016
- _kubb_core.ast.createProperty({
966
+ _kubb_core.ast.factory.createProperty({
1017
967
  name: "headerParams",
1018
- schema: headerParams.length > 0 ? _kubb_core.ast.createSchema({
968
+ schema: headerParams.length > 0 ? _kubb_core.ast.factory.createSchema({
1019
969
  ...buildParams(node, {
1020
970
  params: headerParams,
1021
971
  resolver
1022
972
  }),
1023
973
  optional: true
1024
- }) : _kubb_core.ast.createSchema({
974
+ }) : _kubb_core.ast.factory.createSchema({
1025
975
  type: "never",
1026
976
  primitive: void 0,
1027
977
  optional: true
1028
978
  })
1029
979
  }),
1030
- _kubb_core.ast.createProperty({
980
+ _kubb_core.ast.factory.createProperty({
1031
981
  name: "url",
1032
982
  required: true,
1033
- schema: _kubb_core.ast.createSchema({
983
+ schema: _kubb_core.ast.factory.createSchema({
1034
984
  type: "url",
1035
985
  path: node.path
1036
986
  })
@@ -1040,12 +990,12 @@ function buildData(node, { resolver }) {
1040
990
  }
1041
991
  function buildResponses(node, { resolver }) {
1042
992
  if (node.responses.length === 0) return null;
1043
- return _kubb_core.ast.createSchema({
993
+ return _kubb_core.ast.factory.createSchema({
1044
994
  type: "object",
1045
- properties: node.responses.map((res) => _kubb_core.ast.createProperty({
995
+ properties: node.responses.map((res) => _kubb_core.ast.factory.createProperty({
1046
996
  name: String(res.statusCode),
1047
997
  required: true,
1048
- schema: _kubb_core.ast.createSchema({
998
+ schema: _kubb_core.ast.factory.createSchema({
1049
999
  type: "ref",
1050
1000
  name: resolver.resolveResponseStatusName(node, res.statusCode)
1051
1001
  })
@@ -1055,9 +1005,9 @@ function buildResponses(node, { resolver }) {
1055
1005
  function buildResponseUnion(node, { resolver }) {
1056
1006
  const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
1057
1007
  if (responsesWithSchema.length === 0) return null;
1058
- return _kubb_core.ast.createSchema({
1008
+ return _kubb_core.ast.factory.createSchema({
1059
1009
  type: "union",
1060
- members: responsesWithSchema.map((res) => _kubb_core.ast.createSchema({
1010
+ members: responsesWithSchema.map((res) => _kubb_core.ast.factory.createSchema({
1061
1011
  type: "ref",
1062
1012
  name: resolver.resolveResponseStatusName(node, res.statusCode)
1063
1013
  }))
@@ -1081,13 +1031,13 @@ function isNonNullable(value) {
1081
1031
  *
1082
1032
  * @example Raw type node (no `typeName`)
1083
1033
  * ```ts
1084
- * const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enumType: 'inlineLiteral' })
1034
+ * const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enum: { type: 'inlineLiteral' } })
1085
1035
  * const typeNode = printer.print(schemaNode) // ts.TypeNode
1086
1036
  * ```
1087
1037
  *
1088
1038
  * @example Full declaration (with `typeName`)
1089
1039
  * ```ts
1090
- * const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enumType: 'inlineLiteral', typeName: 'MyType' })
1040
+ * const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enum: { type: 'inlineLiteral' }, typeName: 'MyType' })
1091
1041
  * const declaration = printer.print(schemaNode) // ts.TypeAliasDeclaration | ts.InterfaceDeclaration
1092
1042
  * ```
1093
1043
  */
@@ -1121,27 +1071,27 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
1121
1071
  time: dateOrStringNode,
1122
1072
  ref(node) {
1123
1073
  if (!node.name) return null;
1124
- const refName = node.ref ? _kubb_core.ast.extractRefName(node.ref) ?? node.name : node.name;
1125
- return createTypeReferenceNode(node.ref && ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enumType) && this.options.enumTypeSuffix && this.options.enumSchemaNames?.has(refName) ? this.options.resolver.resolveEnumKeyName({ name: refName }, this.options.enumTypeSuffix) : node.ref ? this.options.resolver.default(refName, "type") : refName, void 0);
1074
+ const refName = node.ref ? (0, _kubb_ast_utils.extractRefName)(node.ref) ?? node.name : node.name;
1075
+ return createTypeReferenceNode(node.ref && ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enum.type) && this.options.enum.typeSuffix && this.options.enumSchemaNames?.has(refName) ? this.options.resolver.resolveEnumKeyName({ name: refName }, this.options.enum.typeSuffix) : node.ref ? this.options.resolver.default(refName, "type") : refName, void 0);
1126
1076
  },
1127
1077
  enum(node) {
1128
1078
  const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? [];
1129
- if (this.options.enumType === "inlineLiteral" || !node.name) return createUnionDeclaration({
1079
+ if (this.options.enum.type === "inlineLiteral" || !node.name) return createUnionDeclaration({
1130
1080
  withParentheses: true,
1131
1081
  nodes: values.filter((v) => v !== null && v !== void 0).map((value) => constToTypeNode(value, typeof value)).filter(isNonNullable)
1132
1082
  }) ?? void 0;
1133
- return createTypeReferenceNode(ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enumType) && this.options.enumTypeSuffix ? this.options.resolver.resolveEnumKeyName(node, this.options.enumTypeSuffix) : this.options.resolver.default(node.name, "type"), void 0);
1083
+ return createTypeReferenceNode(ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enum.type) && this.options.enum.typeSuffix ? this.options.resolver.resolveEnumKeyName(node, this.options.enum.typeSuffix) : this.options.resolver.default(node.name, "type"), void 0);
1134
1084
  },
1135
1085
  union(node) {
1136
1086
  const members = node.members ?? [];
1137
1087
  const hasStringLiteral = members.some((m) => {
1138
1088
  return _kubb_core.ast.narrowSchema(m, _kubb_core.ast.schemaTypes.enum)?.primitive === "string";
1139
1089
  });
1140
- const hasPlainString = members.some((m) => _kubb_core.ast.isStringType(m));
1090
+ const hasPlainString = members.some((m) => (0, _kubb_ast_utils.isStringType)(m));
1141
1091
  if (hasStringLiteral && hasPlainString) return createUnionDeclaration({
1142
1092
  withParentheses: true,
1143
1093
  nodes: members.map((m) => {
1144
- if (_kubb_core.ast.isStringType(m)) return createIntersectionDeclaration({
1094
+ if ((0, _kubb_ast_utils.isStringType)(m)) return createIntersectionDeclaration({
1145
1095
  nodes: [keywordTypeNodes.string, createTypeLiteralNode([])],
1146
1096
  withParentheses: true
1147
1097
  });
@@ -1161,7 +1111,7 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
1161
1111
  },
1162
1112
  array(node) {
1163
1113
  return createArrayDeclaration({
1164
- nodes: (node.items ?? []).map((item) => this.transform(item)).filter(isNonNullable),
1114
+ nodes: (0, _kubb_ast_utils.mapSchemaItems)(node, (item) => this.transform(item)).map(({ output }) => output).filter(isNonNullable),
1165
1115
  arrayType: this.options.arrayType
1166
1116
  }) ?? null;
1167
1117
  },
@@ -1171,18 +1121,19 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
1171
1121
  object(node) {
1172
1122
  const { transform, options } = this;
1173
1123
  const addsQuestionToken = OPTIONAL_ADDS_QUESTION_TOKEN.has(options.optionalType);
1174
- const propertyNodes = node.properties.map((prop) => {
1175
- const baseType = transform(prop.schema) ?? keywordTypeNodes.unknown;
1176
- const type = buildPropertyType(prop.schema, baseType, options.optionalType);
1177
- const propMeta = _kubb_core.ast.syncSchemaRef(prop.schema);
1124
+ const propertyNodes = (0, _kubb_ast_utils.mapSchemaProperties)(node, (schema) => transform(schema)).map(({ name, property, output }) => {
1125
+ const baseType = output ?? keywordTypeNodes.unknown;
1126
+ const optional = !property.required || !!property.schema.optional || !!property.schema.nullish;
1127
+ const type = buildPropertyType(property.schema, baseType, options.optionalType, optional);
1128
+ const propMeta = (0, _kubb_ast_utils.syncSchemaRef)(property.schema);
1178
1129
  return appendJSDocToNode({
1179
1130
  node: createPropertySignature({
1180
- questionToken: prop.schema.optional || prop.schema.nullish ? addsQuestionToken : false,
1181
- name: prop.name,
1131
+ questionToken: optional ? addsQuestionToken : false,
1132
+ name,
1182
1133
  type,
1183
1134
  readOnly: propMeta?.readOnly
1184
1135
  }),
1185
- comments: buildPropertyJSDocComments(prop.schema)
1136
+ comments: buildPropertyJSDocComments(property.schema, optional)
1186
1137
  });
1187
1138
  });
1188
1139
  const allElements = [...propertyNodes, ...buildIndexSignatures(node, propertyNodes.length, transform)];
@@ -1195,7 +1146,7 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
1195
1146
  const { name, syntaxType = "type", description, keysToOmit } = this.options;
1196
1147
  const transformed = this.transform(node);
1197
1148
  if (!transformed) return null;
1198
- const meta = _kubb_core.ast.syncSchemaRef(node);
1149
+ const meta = (0, _kubb_ast_utils.syncSchemaRef)(node);
1199
1150
  if (!name) {
1200
1151
  const withNullable = meta.nullable ? createUnionDeclaration({ nodes: [transformed, keywordTypeNodes.null] }) : transformed;
1201
1152
  const result = (meta.nullish || meta.optional) && addsUndefined ? createUnionDeclaration({ nodes: [withNullable, keywordTypeNodes.undefined] }) : withNullable;
@@ -1234,15 +1185,14 @@ const printerTs = _kubb_core.ast.definePrinter((options) => {
1234
1185
  */
1235
1186
  const typeGenerator = (0, _kubb_core.defineGenerator)({
1236
1187
  name: "typescript",
1237
- renderer: _kubb_renderer_jsx.jsxRendererSync,
1188
+ renderer: _kubb_renderer_jsx.jsxRenderer,
1238
1189
  schema(node, ctx) {
1239
- const { enumType, enumTypeSuffix, enumKeyCasing, syntaxType, optionalType, arrayType, output, group, printer } = ctx.options;
1190
+ const { enum: enumOptions, syntaxType, optionalType, arrayType, output, group, printer } = ctx.options;
1240
1191
  const { adapter, config, resolver, root } = ctx;
1241
1192
  if (!node.name) return;
1242
- const mode = ctx.getMode(output);
1243
1193
  const enumSchemaNames = new Set(ctx.meta.enumNames);
1244
1194
  function resolveImportName(schemaName) {
1245
- if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumTypeSuffix);
1195
+ if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && enumOptions.typeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumOptions.typeSuffix);
1246
1196
  return resolver.resolveTypeName(schemaName);
1247
1197
  }
1248
1198
  const imports = adapter.getImports(node, (schemaName) => ({
@@ -1258,7 +1208,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1258
1208
  }));
1259
1209
  const isEnumSchema = !!_kubb_core.ast.narrowSchema(node, _kubb_core.ast.schemaTypes.enum);
1260
1210
  const meta = {
1261
- name: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && isEnumSchema ? resolver.resolveEnumKeyName(node, enumTypeSuffix) : resolver.resolveTypeName(node.name),
1211
+ name: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && isEnumSchema ? resolver.resolveEnumKeyName(node, enumOptions.typeSuffix) : resolver.resolveTypeName(node.name),
1262
1212
  file: resolver.resolveFile({
1263
1213
  name: node.name,
1264
1214
  extname: ".ts"
@@ -1271,8 +1221,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1271
1221
  const schemaPrinter = printerTs({
1272
1222
  optionalType,
1273
1223
  arrayType,
1274
- enumType,
1275
- enumTypeSuffix,
1224
+ enum: enumOptions,
1276
1225
  name: meta.name,
1277
1226
  syntaxType,
1278
1227
  description: node.description,
@@ -1300,7 +1249,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1300
1249
  baseName: meta.file.baseName
1301
1250
  }
1302
1251
  }),
1303
- children: [mode === "split" && imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1252
+ children: [imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1304
1253
  root: meta.file.path,
1305
1254
  path: imp.path,
1306
1255
  name: imp.name,
@@ -1312,19 +1261,16 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1312
1261
  ].join("-"))), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Type, {
1313
1262
  name: meta.name,
1314
1263
  node,
1315
- enumType,
1316
- enumTypeSuffix,
1317
- enumKeyCasing,
1264
+ enum: enumOptions,
1318
1265
  resolver,
1319
1266
  printer: schemaPrinter
1320
1267
  })]
1321
1268
  });
1322
1269
  },
1323
1270
  operation(node, ctx) {
1324
- const { enumType, enumTypeSuffix, enumKeyCasing, optionalType, arrayType, syntaxType, paramsCasing, group, output, printer } = ctx.options;
1271
+ const { enum: enumOptions, optionalType, arrayType, syntaxType, paramsCasing, group, output, printer } = ctx.options;
1325
1272
  const { adapter, config, resolver, root } = ctx;
1326
- const mode = ctx.getMode(output);
1327
- const params = _kubb_core.ast.caseParams(node.parameters, paramsCasing);
1273
+ const params = (0, _kubb_ast_utils.caseParams)(node.parameters, paramsCasing);
1328
1274
  const meta = { file: resolver.resolveFile({
1329
1275
  name: node.operationId,
1330
1276
  extname: ".ts",
@@ -1337,7 +1283,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1337
1283
  }) };
1338
1284
  const enumSchemaNames = new Set(ctx.meta.enumNames);
1339
1285
  function resolveImportName(schemaName) {
1340
- if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumType) && enumTypeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumTypeSuffix);
1286
+ if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && enumOptions.typeSuffix && enumSchemaNames.has(schemaName)) return resolver.resolveEnumKeyName({ name: schemaName }, enumOptions.typeSuffix);
1341
1287
  return resolver.resolveTypeName(schemaName);
1342
1288
  }
1343
1289
  function renderSchemaType({ schema, name, keysToOmit }) {
@@ -1356,8 +1302,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1356
1302
  const schemaPrinter = printerTs({
1357
1303
  optionalType,
1358
1304
  arrayType,
1359
- enumType,
1360
- enumTypeSuffix,
1305
+ enum: enumOptions,
1361
1306
  name,
1362
1307
  syntaxType,
1363
1308
  description: schema.description,
@@ -1366,7 +1311,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1366
1311
  enumSchemaNames,
1367
1312
  nodes: printer?.nodes
1368
1313
  });
1369
- return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [mode === "split" && imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1314
+ return /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsxs)(_kubb_renderer_jsx_jsx_runtime.Fragment, { children: [imports.map((imp) => /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(_kubb_renderer_jsx.File.Import, {
1370
1315
  root: meta.file.path,
1371
1316
  path: imp.path,
1372
1317
  name: imp.name,
@@ -1378,9 +1323,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1378
1323
  ].join("-"))), /* @__PURE__ */ (0, _kubb_renderer_jsx_jsx_runtime.jsx)(Type, {
1379
1324
  name,
1380
1325
  node: schema,
1381
- enumType,
1382
- enumTypeSuffix,
1383
- enumKeyCasing,
1326
+ enum: enumOptions,
1384
1327
  resolver,
1385
1328
  printer: schemaPrinter
1386
1329
  })] });
@@ -1391,9 +1334,9 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1391
1334
  */
1392
1335
  function buildContentTypeVariants(entries, baseName, decorate) {
1393
1336
  const variants = resolveContentTypeVariants(entries, baseName);
1394
- const unionSchema = _kubb_core.ast.createSchema({
1337
+ const unionSchema = _kubb_core.ast.factory.createSchema({
1395
1338
  type: "union",
1396
- members: variants.map((variant) => _kubb_core.ast.createSchema({
1339
+ members: variants.map((variant) => _kubb_core.ast.factory.createSchema({
1397
1340
  type: "ref",
1398
1341
  name: variant.name
1399
1342
  }))
@@ -1408,7 +1351,10 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1408
1351
  })] });
1409
1352
  }
1410
1353
  const paramTypes = params.map((param) => renderSchemaType({
1411
- schema: param.schema,
1354
+ schema: {
1355
+ ...param.schema,
1356
+ optional: !param.required
1357
+ },
1412
1358
  name: resolver.resolveParamName(node, param)
1413
1359
  }));
1414
1360
  const requestBodyContent = node.requestBody?.content ?? [];
@@ -1511,7 +1457,7 @@ const typeGenerator = (0, _kubb_core.defineGenerator)({
1511
1457
  * casing/file-layout rules.
1512
1458
  *
1513
1459
  * The `default` method is supplied by `defineResolver`. It uses PascalCase for
1514
- * type names and PascalCase-with-isFile for files.
1460
+ * type names and PascalCase file paths (dotted names become `/`-joined) for files.
1515
1461
  *
1516
1462
  * @example Resolve a type and file name
1517
1463
  * ```ts
@@ -1527,15 +1473,15 @@ const resolverTs = (0, _kubb_core.defineResolver)(() => {
1527
1473
  name: "default",
1528
1474
  pluginName: "plugin-ts",
1529
1475
  default(name, type) {
1530
- const resolved = pascalCase(name, { isFile: type === "file" });
1531
- return type === "file" ? resolved : ensureValidVarName(resolved);
1476
+ if (type === "file") return toFilePath(name, pascalCase);
1477
+ return ensureValidVarName(pascalCase(name));
1532
1478
  },
1533
1479
  resolveTypeName(name) {
1534
1480
  return ensureValidVarName(pascalCase(name));
1535
1481
  },
1536
1482
  resolvePathName(name, type) {
1537
- const resolved = pascalCase(name, { isFile: type === "file" });
1538
- return type === "file" ? resolved : ensureValidVarName(resolved);
1483
+ if (type === "file") return toFilePath(name, pascalCase);
1484
+ return ensureValidVarName(pascalCase(name));
1539
1485
  },
1540
1486
  resolveParamName(node, param) {
1541
1487
  return this.resolveTypeName(`${node.operationId} ${param.in} ${param.name}`);
@@ -1593,7 +1539,7 @@ const pluginTsName = "plugin-ts";
1593
1539
  * plugins: [
1594
1540
  * pluginTs({
1595
1541
  * output: { path: './types' },
1596
- * enumType: 'asConst',
1542
+ * enum: { type: 'asConst' },
1597
1543
  * optionalType: 'questionTokenAndUndefined',
1598
1544
  * }),
1599
1545
  * ],
@@ -1603,9 +1549,15 @@ const pluginTsName = "plugin-ts";
1603
1549
  const pluginTs = (0, _kubb_core.definePlugin)((options) => {
1604
1550
  const { output = {
1605
1551
  path: "types",
1606
- barrelType: "named"
1607
- }, group, exclude = [], include, override = [], enumType = "asConst", enumTypeSuffix = "Key", enumKeyCasing = "none", optionalType = "questionToken", arrayType = "array", syntaxType = "type", paramsCasing, printer, resolver: userResolver, transformer: userTransformer, generators: userGenerators = [] } = options;
1608
- const groupConfig = createGroupConfig(group, { suffix: "Controller" });
1552
+ barrel: { type: "named" }
1553
+ }, group, exclude = [], include, override = [], enum: enumOptions = {}, optionalType = "questionToken", arrayType = "array", syntaxType = "type", paramsCasing, printer, resolver: userResolver, macros: userMacros, generators: userGenerators = [] } = options;
1554
+ const groupConfig = createGroupConfig(group);
1555
+ const resolvedEnum = {
1556
+ type: enumOptions.type ?? "asConst",
1557
+ constCasing: enumOptions.constCasing ?? "camelCase",
1558
+ typeSuffix: enumOptions.typeSuffix ?? "Key",
1559
+ keyCasing: enumOptions.keyCasing ?? "none"
1560
+ };
1609
1561
  return {
1610
1562
  name: pluginTsName,
1611
1563
  options,
@@ -1618,9 +1570,7 @@ const pluginTs = (0, _kubb_core.definePlugin)((options) => {
1618
1570
  optionalType,
1619
1571
  group: groupConfig,
1620
1572
  arrayType,
1621
- enumType,
1622
- enumTypeSuffix,
1623
- enumKeyCasing,
1573
+ enum: resolvedEnum,
1624
1574
  syntaxType,
1625
1575
  paramsCasing,
1626
1576
  printer
@@ -1629,7 +1579,7 @@ const pluginTs = (0, _kubb_core.definePlugin)((options) => {
1629
1579
  ...resolverTs,
1630
1580
  ...userResolver
1631
1581
  } : resolverTs);
1632
- if (userTransformer) ctx.setTransformer(userTransformer);
1582
+ if (userMacros?.length) ctx.setMacros(userMacros);
1633
1583
  ctx.addGenerator(typeGenerator);
1634
1584
  for (const gen of userGenerators) ctx.addGenerator(gen);
1635
1585
  } }
@@ -1637,37 +1587,75 @@ const pluginTs = (0, _kubb_core.definePlugin)((options) => {
1637
1587
  });
1638
1588
  //#endregion
1639
1589
  //#region src/printers/functionPrinter.ts
1640
- const kindToHandlerKey = {
1641
- FunctionParameter: "functionParameter",
1642
- ParameterGroup: "parameterGroup",
1643
- FunctionParameters: "functionParameters",
1644
- ParamsType: "paramsType"
1645
- };
1646
1590
  /**
1647
- * Creates a function-parameter printer factory.
1591
+ * Renders a {@link ast.TypeExpression} to its TypeScript source.
1592
+ *
1593
+ * - a `string` is a type reference, returned as-is
1594
+ * - an `IndexedAccessType` becomes `objectType['indexType']`
1595
+ * - a `TypeLiteral` becomes `{ key: Type; key?: Type }`
1648
1596
  *
1649
- * Uses `createPrinterFactory` and dispatches handlers by `node.kind`
1650
- * (for function nodes) rather than by `node.type` (for schema nodes).
1597
+ * `transformType` is applied once to the fully rendered type, matching how the
1598
+ * printer wrapped reference types before the `ts.factory` model.
1651
1599
  */
1652
- const defineFunctionPrinter = _kubb_core.ast.createPrinterFactory((node) => kindToHandlerKey[node.kind]);
1600
+ function renderType(type, transformType) {
1601
+ const rendered = renderTypeExpression(type);
1602
+ return transformType ? transformType(rendered) : rendered;
1603
+ }
1604
+ function renderTypeExpression(type) {
1605
+ if (typeof type === "string") return type;
1606
+ if (_kubb_core.ast.indexedAccessTypeDef.is(type)) return `${type.target}['${type.key}']`;
1607
+ return `{ ${type.members.map((member) => {
1608
+ const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(member.name) ? member.name : JSON.stringify(member.name);
1609
+ const value = renderTypeExpression(member.type);
1610
+ return member.optional ? `${key}?: ${value}` : `${key}: ${value}`;
1611
+ }).join("; ")} }`;
1612
+ }
1613
+ function groupMembers(param) {
1614
+ if (typeof param.name === "string") return null;
1615
+ return param.type && _kubb_core.ast.typeLiteralDef.is(param.type) ? param.type.members : [];
1616
+ }
1653
1617
  function rank(param) {
1654
- if (param.kind === "ParameterGroup") {
1655
- if (param.default) return PARAM_RANK.withDefault;
1656
- return param.optional ?? param.properties.every((p) => p.optional || p.default !== void 0) ? PARAM_RANK.optional : PARAM_RANK.required;
1657
- }
1658
1618
  if (param.rest) return PARAM_RANK.rest;
1659
1619
  if (param.default) return PARAM_RANK.withDefault;
1620
+ const members = groupMembers(param);
1621
+ if (members) return members.every((m) => m.optional) ? PARAM_RANK.optional : PARAM_RANK.required;
1660
1622
  return param.optional ? PARAM_RANK.optional : PARAM_RANK.required;
1661
1623
  }
1662
1624
  function sortParams(params) {
1663
1625
  return params.toSorted((a, b) => rank(a) - rank(b));
1664
1626
  }
1665
- function sortChildParams(params) {
1666
- return params.toSorted((a, b) => rank(a) - rank(b));
1627
+ /**
1628
+ * Orders a destructured group's binding elements and type members together,
1629
+ * required fields first, matching how grouped children were sorted before.
1630
+ */
1631
+ function sortedGroupMembers(name, type) {
1632
+ const members = type && _kubb_core.ast.typeLiteralDef.is(type) ? type.members : [];
1633
+ const memberRank = (optional) => optional ? PARAM_RANK.optional : PARAM_RANK.required;
1634
+ return name.elements.map((element, index) => ({
1635
+ name: element.name,
1636
+ type: members[index]?.type,
1637
+ optional: members[index]?.optional
1638
+ })).toSorted((a, b) => memberRank(a.optional) - memberRank(b.optional));
1639
+ }
1640
+ /**
1641
+ * Renders the type annotation of a destructured group. An inline object type is
1642
+ * rendered from the already-sorted members so its key order matches the binding;
1643
+ * a reference type is rendered as-is.
1644
+ */
1645
+ function renderGroupType(type, sorted, transformType) {
1646
+ if (!type) return void 0;
1647
+ if (!_kubb_core.ast.typeLiteralDef.is(type)) return renderType(type);
1648
+ const typed = sorted.filter((member) => member.type !== void 0);
1649
+ if (!typed.length) return void 0;
1650
+ return `{ ${typed.map((member) => {
1651
+ const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(member.name) ? member.name : JSON.stringify(member.name);
1652
+ const value = renderType(member.type, transformType);
1653
+ return member.optional ? `${key}?: ${value}` : `${key}: ${value}`;
1654
+ }).join("; ")} }`;
1667
1655
  }
1668
1656
  /**
1669
- * Default function-signature printer.
1670
- * Covers the four standard output modes used across Kubb plugins.
1657
+ * Default function-signature printer. Renders a parameter list in one of two modes:
1658
+ * `declaration` for the function signature and `call` for the call arguments.
1671
1659
  *
1672
1660
  * @example
1673
1661
  * ```ts
@@ -1683,28 +1671,31 @@ function sortChildParams(params) {
1683
1671
  * printer.print(sig) // → "petId: string, config: Config = {}"
1684
1672
  * ```
1685
1673
  */
1686
- const functionPrinter = defineFunctionPrinter((options) => ({
1674
+ const functionPrinter = _kubb_core.ast.createPrinterFactory((node) => node.kind)((options) => ({
1687
1675
  name: "functionParameters",
1688
1676
  options,
1689
1677
  nodes: {
1690
- paramsType(node) {
1691
- if (node.kind !== "ParamsType") return null;
1692
- if (node.variant === "member") return `${node.base}['${node.key}']`;
1693
- if (node.variant === "struct") return `{ ${node.properties.map((p) => {
1694
- const typeStr = this.transform(p.type);
1695
- const key = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(p.name) ? p.name : JSON.stringify(p.name);
1696
- return p.optional ? `${key}?: ${typeStr}` : `${key}: ${typeStr}`;
1697
- }).join("; ")} }`;
1698
- if (node.variant === "reference") return node.name;
1699
- return null;
1700
- },
1701
- functionParameter(node) {
1678
+ FunctionParameter(node) {
1702
1679
  const { mode, transformName, transformType } = this.options;
1680
+ const isGroup = typeof node.name !== "string";
1681
+ if (mode === "call") {
1682
+ if (isGroup) return `{ ${sortedGroupMembers(node.name, node.type).map((member) => member.name).join(", ")} }`;
1683
+ const name = transformName ? transformName(node.name) : node.name;
1684
+ return node.rest ? `...${name}` : name;
1685
+ }
1686
+ if (isGroup) {
1687
+ const sorted = sortedGroupMembers(node.name, node.type);
1688
+ const binding = `{ ${sorted.map((member) => transformName ? transformName(member.name) : member.name).join(", ")} }`;
1689
+ const allOptional = sorted.every((member) => member.optional);
1690
+ const type = renderGroupType(node.type, sorted, transformType);
1691
+ if (type) {
1692
+ if (allOptional) return `${binding}: ${type} = ${node.default ?? "{}"}`;
1693
+ return node.default ? `${binding}: ${type} = ${node.default}` : `${binding}: ${type}`;
1694
+ }
1695
+ return node.default ? `${binding} = ${node.default}` : binding;
1696
+ }
1703
1697
  const name = transformName ? transformName(node.name) : node.name;
1704
- const rawType = node.type ? this.transform(node.type) : void 0;
1705
- const type = rawType != null && transformType ? transformType(rawType) : rawType;
1706
- if (mode === "keys" || mode === "values") return node.rest ? `...${name}` : name;
1707
- if (mode === "call") return node.rest ? `...${name}` : name;
1698
+ const type = node.type ? renderType(node.type, transformType) : void 0;
1708
1699
  if (node.rest) return type ? `...${name}: ${type}` : `...${name}`;
1709
1700
  if (type) {
1710
1701
  if (node.optional) return `${name}?: ${type}`;
@@ -1712,34 +1703,7 @@ const functionPrinter = defineFunctionPrinter((options) => ({
1712
1703
  }
1713
1704
  return node.default ? `${name} = ${node.default}` : name;
1714
1705
  },
1715
- parameterGroup(node) {
1716
- const { mode, transformName, transformType } = this.options;
1717
- const sorted = sortChildParams(node.properties);
1718
- const isOptional = node.optional ?? sorted.every((p) => p.optional || p.default !== void 0);
1719
- if (node.inline) return sorted.map((p) => this.transform(p)).filter(Boolean).join(", ");
1720
- if (mode === "keys" || mode === "values") return `{ ${sorted.map((p) => p.name).join(", ")} }`;
1721
- if (mode === "call") return `{ ${sorted.map((p) => p.name).join(", ")} }`;
1722
- const names = sorted.map((p) => {
1723
- return transformName ? transformName(p.name) : p.name;
1724
- });
1725
- const nameStr = names.length ? `{ ${names.join(", ")} }` : void 0;
1726
- if (!nameStr) return null;
1727
- let typeAnnotation = node.type ? this.transform(node.type) ?? void 0 : void 0;
1728
- if (!typeAnnotation) {
1729
- const typeParts = sorted.filter((p) => p.type).map((p) => {
1730
- const rawT = p.type ? this.transform(p.type) : void 0;
1731
- const t = rawT != null && transformType ? transformType(rawT) : rawT;
1732
- return p.optional || p.default !== void 0 ? `${p.name}?: ${t}` : `${p.name}: ${t}`;
1733
- });
1734
- typeAnnotation = typeParts.length ? `{ ${typeParts.join("; ")} }` : void 0;
1735
- }
1736
- if (typeAnnotation) {
1737
- if (isOptional) return `${nameStr}: ${typeAnnotation} = ${node.default ?? "{}"}`;
1738
- return node.default ? `${nameStr}: ${typeAnnotation} = ${node.default}` : `${nameStr}: ${typeAnnotation}`;
1739
- }
1740
- return node.default ? `${nameStr} = ${node.default}` : nameStr;
1741
- },
1742
- functionParameters(node) {
1706
+ FunctionParameters(node) {
1743
1707
  return sortParams(node.params).map((p) => this.transform(p)).filter(Boolean).join(", ");
1744
1708
  }
1745
1709
  }
@@ -1752,6 +1716,7 @@ exports.functionPrinter = functionPrinter;
1752
1716
  exports.pluginTs = pluginTs;
1753
1717
  exports.pluginTsName = pluginTsName;
1754
1718
  exports.printerTs = printerTs;
1719
+ exports.renderType = renderType;
1755
1720
  exports.resolverTs = resolverTs;
1756
1721
  exports.typeGenerator = typeGenerator;
1757
1722