@kubb/plugin-ts 5.0.0-alpha.9 → 5.0.0-beta.100

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.
Files changed (41) hide show
  1. package/LICENSE +17 -10
  2. package/README.md +38 -23
  3. package/dist/index.cjs +2376 -5
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.ts +941 -40
  6. package/dist/index.js +2321 -2
  7. package/dist/index.js.map +1 -0
  8. package/dist/rolldown-runtime-CNktS9qV.js +17 -0
  9. package/package.json +38 -74
  10. package/dist/chunk--u3MIqq1.js +0 -8
  11. package/dist/components-CRu8IKY3.js +0 -729
  12. package/dist/components-CRu8IKY3.js.map +0 -1
  13. package/dist/components-DeNDKlzf.cjs +0 -982
  14. package/dist/components-DeNDKlzf.cjs.map +0 -1
  15. package/dist/components.cjs +0 -3
  16. package/dist/components.d.ts +0 -36
  17. package/dist/components.js +0 -2
  18. package/dist/generators.cjs +0 -4
  19. package/dist/generators.d.ts +0 -509
  20. package/dist/generators.js +0 -2
  21. package/dist/plugin-BZkBwnEA.js +0 -1269
  22. package/dist/plugin-BZkBwnEA.js.map +0 -1
  23. package/dist/plugin-Bunz1oGa.cjs +0 -1322
  24. package/dist/plugin-Bunz1oGa.cjs.map +0 -1
  25. package/dist/types-mSXmB8WU.d.ts +0 -298
  26. package/src/components/Type.tsx +0 -190
  27. package/src/components/index.ts +0 -1
  28. package/src/components/v2/Enum.tsx +0 -67
  29. package/src/components/v2/Type.tsx +0 -59
  30. package/src/constants.ts +0 -29
  31. package/src/factory.ts +0 -726
  32. package/src/generators/index.ts +0 -2
  33. package/src/generators/typeGenerator.tsx +0 -468
  34. package/src/generators/v2/typeGenerator.tsx +0 -167
  35. package/src/generators/v2/utils.ts +0 -140
  36. package/src/index.ts +0 -3
  37. package/src/parser.ts +0 -389
  38. package/src/plugin.ts +0 -232
  39. package/src/printer.ts +0 -368
  40. package/src/resolverTs.ts +0 -77
  41. package/src/types.ts +0 -296
package/dist/index.js CHANGED
@@ -1,2 +1,2321 @@
1
- import { i as resolverTs, n as pluginTsName, t as pluginTs } from "./plugin-BZkBwnEA.js";
2
- export { pluginTs, pluginTsName, resolverTs };
1
+ import { n as __name, t as __exportAll } from "./rolldown-runtime-CNktS9qV.js";
2
+ import { parserTs } from "@kubb/parser-ts";
3
+ import { File, jsxRenderer } from "kubb/jsx";
4
+ import { Resolver, ast, createResolver, defineGenerator, definePlugin, isStringType, syncSchemaRef } from "kubb/kit";
5
+ import ts from "typescript";
6
+ import { Fragment, jsx, jsxs } from "kubb/jsx/jsx-runtime";
7
+ //#region ../../internals/utils/src/casing.ts
8
+ /**
9
+ * Shared implementation for camelCase and PascalCase conversion.
10
+ * Splits on common word boundaries (spaces, hyphens, underscores, dots, slashes, colons)
11
+ * and capitalizes each word according to `pascal`.
12
+ *
13
+ * When `pascal` is `true` the first word is also capitalized (PascalCase), otherwise only subsequent words are.
14
+ */
15
+ function toCamelOrPascal(text, pascal) {
16
+ 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) => {
17
+ if (word.length > 1 && word === word.toUpperCase()) return word;
18
+ return (i === 0 && !pascal ? word.charAt(0).toLowerCase() : word.charAt(0).toUpperCase()) + word.slice(1);
19
+ }).join("").replace(/[^a-zA-Z0-9]/g, "");
20
+ }
21
+ /**
22
+ * Converts `text` to camelCase.
23
+ *
24
+ * @example Word boundaries
25
+ * `camelCase('hello-world') // 'helloWorld'`
26
+ *
27
+ * @example With a prefix
28
+ * `camelCase('tag', { prefix: 'create' }) // 'createTag'`
29
+ */
30
+ function camelCase(text, { prefix = "", suffix = "" } = {}) {
31
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, false);
32
+ }
33
+ /**
34
+ * Converts `text` to PascalCase.
35
+ *
36
+ * @example Word boundaries
37
+ * `pascalCase('hello-world') // 'HelloWorld'`
38
+ *
39
+ * @example With a suffix
40
+ * `pascalCase('tag', { suffix: 'schema' }) // 'TagSchema'`
41
+ */
42
+ function pascalCase(text, { prefix = "", suffix = "" } = {}) {
43
+ return toCamelOrPascal(`${prefix} ${text} ${suffix}`, true);
44
+ }
45
+ /**
46
+ * Converts `text` to snake_case.
47
+ *
48
+ * @example From camelCase
49
+ * `snakeCase('helloWorld') // 'hello_world'`
50
+ *
51
+ * @example From mixed separators
52
+ * `snakeCase('Hello-World') // 'hello_world'`
53
+ */
54
+ function snakeCase(text, { prefix = "", suffix = "" } = {}) {
55
+ 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("_");
56
+ }
57
+ /**
58
+ * Converts `text` to SCREAMING_SNAKE_CASE.
59
+ *
60
+ * @example From camelCase
61
+ * `screamingSnakeCase('helloWorld') // 'HELLO_WORLD'`
62
+ */
63
+ function screamingSnakeCase(text, { prefix = "", suffix = "" } = {}) {
64
+ return snakeCase(text, {
65
+ prefix,
66
+ suffix
67
+ }).toUpperCase();
68
+ }
69
+ //#endregion
70
+ //#region ../../internals/utils/src/reserved.ts
71
+ /**
72
+ * JavaScript and Java reserved words.
73
+ * @link https://github.com/jonschlinkert/reserved/blob/master/index.js
74
+ */
75
+ const reservedWords = /* @__PURE__ */ new Set([
76
+ "abstract",
77
+ "arguments",
78
+ "boolean",
79
+ "break",
80
+ "byte",
81
+ "case",
82
+ "catch",
83
+ "char",
84
+ "class",
85
+ "const",
86
+ "continue",
87
+ "debugger",
88
+ "default",
89
+ "delete",
90
+ "do",
91
+ "double",
92
+ "else",
93
+ "enum",
94
+ "eval",
95
+ "export",
96
+ "extends",
97
+ "false",
98
+ "final",
99
+ "finally",
100
+ "float",
101
+ "for",
102
+ "function",
103
+ "goto",
104
+ "if",
105
+ "implements",
106
+ "import",
107
+ "in",
108
+ "instanceof",
109
+ "int",
110
+ "interface",
111
+ "let",
112
+ "long",
113
+ "native",
114
+ "new",
115
+ "null",
116
+ "package",
117
+ "private",
118
+ "protected",
119
+ "public",
120
+ "return",
121
+ "short",
122
+ "static",
123
+ "super",
124
+ "switch",
125
+ "synchronized",
126
+ "this",
127
+ "throw",
128
+ "throws",
129
+ "transient",
130
+ "true",
131
+ "try",
132
+ "typeof",
133
+ "var",
134
+ "void",
135
+ "volatile",
136
+ "while",
137
+ "with",
138
+ "yield",
139
+ "Array",
140
+ "Date",
141
+ "hasOwnProperty",
142
+ "Infinity",
143
+ "isFinite",
144
+ "isNaN",
145
+ "isPrototypeOf",
146
+ "length",
147
+ "Math",
148
+ "name",
149
+ "NaN",
150
+ "Number",
151
+ "Object",
152
+ "prototype",
153
+ "String",
154
+ "toString",
155
+ "undefined",
156
+ "valueOf"
157
+ ]);
158
+ /**
159
+ * Returns `true` when `name` is a syntactically valid JavaScript variable name.
160
+ *
161
+ * @example
162
+ * ```ts
163
+ * isValidVarName('status') // true
164
+ * isValidVarName('class') // false (reserved word)
165
+ * isValidVarName('42foo') // false (starts with digit)
166
+ * ```
167
+ */
168
+ function isValidVarName(name) {
169
+ if (!name || reservedWords.has(name)) return false;
170
+ return isIdentifier(name);
171
+ }
172
+ /**
173
+ * Returns `name` when it's a syntactically valid JavaScript variable name,
174
+ * otherwise prefixes it with `_` so the result is a valid identifier.
175
+ *
176
+ * Useful for sanitizing OpenAPI schema names or operation IDs that start with
177
+ * a digit (e.g. `409`, `504AccountCancel`) before using them as exported
178
+ * variable, type, or function names.
179
+ *
180
+ * @example
181
+ * ```ts
182
+ * ensureValidVarName('409') // '_409'
183
+ * ensureValidVarName('504AccountCancel') // '_504AccountCancel'
184
+ * ensureValidVarName('Pet') // 'Pet'
185
+ * ensureValidVarName('class') // '_class'
186
+ * ```
187
+ */
188
+ function ensureValidVarName(name) {
189
+ if (!name || isValidVarName(name)) return name;
190
+ return `_${name}`;
191
+ }
192
+ /**
193
+ * Returns `true` when `name` is syntactically a valid identifier, ignoring reserved words.
194
+ *
195
+ * Reserved words and globals (`class`, `name`, `Date`, …) are valid as bare object-literal keys
196
+ * even though they are not valid variable names, so use this (not {@link isValidVarName}) when
197
+ * deciding whether an object key needs quoting.
198
+ *
199
+ * @example
200
+ * ```ts
201
+ * isIdentifier('name') // true
202
+ * isIdentifier('x-total')// false
203
+ * ```
204
+ */
205
+ function isIdentifier(name) {
206
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name);
207
+ }
208
+ //#endregion
209
+ //#region ../../internals/utils/src/strings.ts
210
+ /**
211
+ * Strips a single matching pair of `"..."`, `'...'`, or `` `...` `` from both ends of `text`.
212
+ * Returns the string unchanged when no balanced quote pair is found.
213
+ *
214
+ * @example
215
+ * ```ts
216
+ * trimQuotes('"hello"') // 'hello'
217
+ * trimQuotes('hello') // 'hello'
218
+ * ```
219
+ */
220
+ function trimQuotes(text) {
221
+ if (text.length >= 2) {
222
+ const first = text[0];
223
+ const last = text[text.length - 1];
224
+ if (first === "\"" && last === "\"" || first === "'" && last === "'" || first === "`" && last === "`") return text.slice(1, -1);
225
+ }
226
+ return text;
227
+ }
228
+ /**
229
+ * Serializes a primitive to a single-quoted string literal, stripping any surrounding quotes first.
230
+ *
231
+ * Escaping runs through `JSON.stringify`, then the result switches to single quotes so the generated
232
+ * code matches the repo style without a formatter.
233
+ *
234
+ * @example
235
+ * ```ts
236
+ * stringify('hello') // "'hello'"
237
+ * stringify('"hello"') // "'hello'"
238
+ * ```
239
+ */
240
+ function stringify(value) {
241
+ if (value === void 0 || value === null) return "''";
242
+ return `'${JSON.stringify(trimQuotes(value.toString())).slice(1, -1).replace(/\\"/g, "\"").replace(/'/g, "\\'")}'`;
243
+ }
244
+ /**
245
+ * Escapes characters that are not allowed inside JS string literals, covering quotes, backslashes,
246
+ * and the Unicode line terminators U+2028 and U+2029.
247
+ *
248
+ * @see http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.4
249
+ *
250
+ * @example
251
+ * ```ts
252
+ * jsStringEscape('say "hi"\nbye') // 'say \\"hi\\"\\nbye'
253
+ * ```
254
+ */
255
+ function jsStringEscape(input) {
256
+ return `${input}`.replace(/["'\\\n\r\u2028\u2029]/g, (character) => {
257
+ switch (character) {
258
+ case "\"":
259
+ case "'":
260
+ case "\\": return `\\${character}`;
261
+ case "\n": return "\\n";
262
+ case "\r": return "\\r";
263
+ case "\u2028": return "\\u2028";
264
+ case "\u2029": return "\\u2029";
265
+ default: return "";
266
+ }
267
+ });
268
+ }
269
+ //#endregion
270
+ //#region ../../internals/utils/src/fs.ts
271
+ /**
272
+ * Builds a nested file path from a dotted name. Splits on dots that precede a letter
273
+ * (so version numbers embedded in operationIds like `v2025.0` stay intact), camelCases
274
+ * every earlier segment, applies `caseLast` to the final segment, and joins with `/`.
275
+ *
276
+ * Empty segments are dropped before joining. They arise when the name starts with a dot
277
+ * followed by a letter (e.g. `..Schema` splits into `['..', 'Schema']` and `'..'` cases to
278
+ * an empty string). Without this a leading `/` would form, which `path.resolve` reads as an
279
+ * absolute path, letting generated files escape the configured output directory.
280
+ *
281
+ * @example Nested path from a dotted name
282
+ * `toFilePath('pet.petId') // 'pet/petId'`
283
+ *
284
+ * @example PascalCase the final segment
285
+ * `toFilePath('pet.Pet', pascalCase) // 'pet/Pet'`
286
+ *
287
+ * @example Suffix applied to the final segment only
288
+ * `toFilePath('tag.tag', (part) => camelCase(part, { suffix: 'schema' })) // 'tag/tagSchema'`
289
+ */
290
+ function toFilePath(name, caseLast = camelCase) {
291
+ const parts = name.split(/\.(?=[a-zA-Z])/);
292
+ return parts.map((part, i) => i === parts.length - 1 ? caseLast(part) : camelCase(part)).filter(Boolean).join("/");
293
+ }
294
+ //#endregion
295
+ //#region src/constants.ts
296
+ /**
297
+ * `optionalType` values that cause a property's type to include `| undefined`.
298
+ */
299
+ const OPTIONAL_ADDS_UNDEFINED = /* @__PURE__ */ new Set(["undefined", "questionTokenAndUndefined"]);
300
+ /**
301
+ * `optionalType` values that render the property key with a `?` token.
302
+ */
303
+ const OPTIONAL_ADDS_QUESTION_TOKEN = /* @__PURE__ */ new Set(["questionToken", "questionTokenAndUndefined"]);
304
+ /**
305
+ * `enum.type` values that append a `typeSuffix` to the generated enum type alias.
306
+ */
307
+ const ENUM_TYPES_WITH_KEY_SUFFIX = /* @__PURE__ */ new Set(["asConst"]);
308
+ /**
309
+ * `enum.type` values that require a runtime value declaration (object, enum, or literal).
310
+ */
311
+ const ENUM_TYPES_WITH_RUNTIME_VALUE = /* @__PURE__ */ new Set([
312
+ "enum",
313
+ "asConst",
314
+ "constEnum",
315
+ "literal",
316
+ void 0
317
+ ]);
318
+ /**
319
+ * `enum.type` values whose type declaration is type-only (no runtime value emitted for the type alias).
320
+ */
321
+ const ENUM_TYPES_WITH_TYPE_ONLY = /* @__PURE__ */ new Set([
322
+ "asConst",
323
+ "literal",
324
+ void 0
325
+ ]);
326
+ /**
327
+ * Ordering priority for function parameters: lower = sorted earlier.
328
+ */
329
+ const PARAM_RANK = {
330
+ required: 0,
331
+ optional: 1,
332
+ withDefault: 2,
333
+ rest: 3
334
+ };
335
+ //#endregion
336
+ //#region src/factory.ts
337
+ var factory_exports = /* @__PURE__ */ __exportAll({
338
+ appendJSDocToNode: () => appendJSDocToNode,
339
+ buildIndexSignatures: () => buildIndexSignatures,
340
+ buildMemberNodes: () => buildMemberNodes,
341
+ buildPropertyType: () => buildPropertyType,
342
+ buildTupleNode: () => buildTupleNode,
343
+ constToTypeNode: () => constToTypeNode,
344
+ createArrayDeclaration: () => createArrayDeclaration,
345
+ createEnumDeclaration: () => createEnumDeclaration,
346
+ createExportDeclaration: () => createExportDeclaration,
347
+ createImportDeclaration: () => createImportDeclaration,
348
+ createIntersectionDeclaration: () => createIntersectionDeclaration,
349
+ createJSDoc: () => createJSDoc,
350
+ createOmitDeclaration: () => createOmitDeclaration,
351
+ createParameterSignature: () => createParameterSignature,
352
+ createPropertySignature: () => createPropertySignature,
353
+ createQuestionToken: () => createQuestionToken,
354
+ createTypeDeclaration: () => createTypeDeclaration,
355
+ createTypeLiteralNode: () => createTypeLiteralNode,
356
+ createTypeReferenceNode: () => createTypeReferenceNode,
357
+ createUnionDeclaration: () => createUnionDeclaration,
358
+ createUrlTemplateType: () => createUrlTemplateType,
359
+ dateOrStringNode: () => dateOrStringNode,
360
+ isNonNullable: () => isNonNullable$1,
361
+ keywordTypeNodes: () => keywordTypeNodes,
362
+ modifiers: () => modifiers,
363
+ syntaxKind: () => syntaxKind
364
+ });
365
+ const { SyntaxKind, factory } = ts;
366
+ /**
367
+ * Compares two strings by UTF-16 code unit, keeping sorted output identical across platforms
368
+ * regardless of locale.
369
+ */
370
+ function compareStrings(a, b) {
371
+ if (a < b) return -1;
372
+ if (a > b) return 1;
373
+ return 0;
374
+ }
375
+ function isNumber(value) {
376
+ return typeof value === "number" && !Number.isNaN(value);
377
+ }
378
+ /**
379
+ * TypeScript AST modifiers for common keywords (async, export, const, static).
380
+ */
381
+ const modifiers = {
382
+ async: factory.createModifier(ts.SyntaxKind.AsyncKeyword),
383
+ export: factory.createModifier(ts.SyntaxKind.ExportKeyword),
384
+ const: factory.createModifier(ts.SyntaxKind.ConstKeyword),
385
+ static: factory.createModifier(ts.SyntaxKind.StaticKeyword)
386
+ };
387
+ /**
388
+ * TypeScript syntax kind constants for union, literal, and string types.
389
+ */
390
+ const syntaxKind = {
391
+ union: SyntaxKind.UnionType,
392
+ literalType: SyntaxKind.LiteralType,
393
+ stringLiteral: SyntaxKind.StringLiteral
394
+ };
395
+ function isNonNullable$1(value) {
396
+ return value !== null && value !== void 0;
397
+ }
398
+ __name(isNonNullable$1, "isNonNullable");
399
+ function isValidIdentifier(str) {
400
+ if (!str.length || str.trim() !== str) return false;
401
+ let ch = str.codePointAt(0);
402
+ if (!ts.isIdentifierStart(ch, ts.ScriptTarget.Latest)) return false;
403
+ for (let i = ch > 65535 ? 2 : 1; i < str.length; i += ch > 65535 ? 2 : 1) {
404
+ ch = str.codePointAt(i);
405
+ if (!ts.isIdentifierPart(ch, ts.ScriptTarget.Latest)) return false;
406
+ }
407
+ return true;
408
+ }
409
+ function propertyName(name) {
410
+ if (typeof name === "string") return isValidIdentifier(name) ? factory.createIdentifier(name) : factory.createStringLiteral(name);
411
+ return name;
412
+ }
413
+ const questionToken = factory.createToken(ts.SyntaxKind.QuestionToken);
414
+ /**
415
+ * Creates a question token for optional type annotations.
416
+ * Pass `true` to use the cached token, or provide a pre-created token.
417
+ */
418
+ function createQuestionToken(token) {
419
+ if (!token) return;
420
+ if (token === true) return questionToken;
421
+ return token;
422
+ }
423
+ /**
424
+ * Creates a TypeScript intersection type node from multiple type nodes.
425
+ * Returns the single node if only one is provided, or wraps in parentheses if requested.
426
+ */
427
+ function createIntersectionDeclaration({ nodes, withParentheses }) {
428
+ if (!nodes.length) return null;
429
+ if (nodes.length === 1) return nodes[0] || null;
430
+ const node = factory.createIntersectionTypeNode(nodes);
431
+ if (withParentheses) return factory.createParenthesizedType(node);
432
+ return node;
433
+ }
434
+ /**
435
+ * Creates a TypeScript array type node.
436
+ * Use `arrayType: 'array'` for bracket syntax (`T[]`), or `'generic'` for `Array<T>`.
437
+ *
438
+ * @example Array bracket syntax
439
+ * `createArrayDeclaration({ nodes: [stringType], arrayType: 'array' }) // → string[]`
440
+ *
441
+ * @example Generic Array syntax
442
+ * `createArrayDeclaration({ nodes: [stringType], arrayType: 'generic' }) // → Array<string>`
443
+ */
444
+ function createArrayDeclaration({ nodes, arrayType = "array" }) {
445
+ if (!nodes.length) return factory.createTupleTypeNode([]);
446
+ if (nodes.length === 1) {
447
+ const node = nodes[0];
448
+ if (!node) return null;
449
+ if (arrayType === "generic") return factory.createTypeReferenceNode(factory.createIdentifier("Array"), [node]);
450
+ return factory.createArrayTypeNode(node);
451
+ }
452
+ const unionType = factory.createUnionTypeNode(nodes);
453
+ if (arrayType === "generic") return factory.createTypeReferenceNode(factory.createIdentifier("Array"), [unionType]);
454
+ return factory.createArrayTypeNode(factory.createParenthesizedType(unionType));
455
+ }
456
+ /**
457
+ * Minimum nodes length of 2
458
+ * @example Union type example
459
+ * `string | number`
460
+ */
461
+ function createUnionDeclaration({ nodes, withParentheses }) {
462
+ if (!nodes.length) return keywordTypeNodes.any;
463
+ if (nodes.length === 1) return nodes[0];
464
+ const node = factory.createUnionTypeNode(nodes);
465
+ if (withParentheses) return factory.createParenthesizedType(node);
466
+ return node;
467
+ }
468
+ /**
469
+ * Creates a TypeScript property signature for object/interface members.
470
+ * Supports optional markers, readonly modifiers, and type annotations.
471
+ */
472
+ function createPropertySignature({ readOnly, modifiers = [], name, questionToken, type }) {
473
+ return factory.createPropertySignature([...modifiers, readOnly ? factory.createToken(ts.SyntaxKind.ReadonlyKeyword) : void 0].filter((modifier) => modifier !== void 0), propertyName(name), createQuestionToken(questionToken), type);
474
+ }
475
+ /**
476
+ * Creates a function parameter declaration with optional markers, rest parameters, and type annotations.
477
+ */
478
+ function createParameterSignature(name, { modifiers, dotDotDotToken, questionToken, type, initializer }) {
479
+ return factory.createParameterDeclaration(modifiers, dotDotDotToken, name, createQuestionToken(questionToken), type, initializer);
480
+ }
481
+ /**
482
+ * Creates a JSDoc comment node from an array of comment strings.
483
+ * Returns null if no comments are provided.
484
+ */
485
+ function createJSDoc({ comments }) {
486
+ if (!comments.length) return null;
487
+ return factory.createJSDocComment(factory.createNodeArray(comments.map((comment, i) => {
488
+ if (i === comments.length - 1) return factory.createJSDocText(comment);
489
+ return factory.createJSDocText(`${comment}\n`);
490
+ })));
491
+ }
492
+ /**
493
+ * Attaches JSDoc comments to an AST node as synthetic leading comments.
494
+ * Filters out undefined comments before attaching.
495
+ *
496
+ * @see https://github.com/microsoft/TypeScript/issues/44151
497
+ */
498
+ function appendJSDocToNode({ node, comments }) {
499
+ const filteredComments = comments.filter(Boolean);
500
+ if (!filteredComments.length) return node;
501
+ const text = filteredComments.reduce((acc = "", comment = "") => {
502
+ return `${acc}\n * ${comment.replaceAll("*/", "*\\/")}`;
503
+ }, "*");
504
+ return ts.addSyntheticLeadingComment(node, ts.SyntaxKind.MultiLineCommentTrivia, `${text || "*"}\n`, true);
505
+ }
506
+ /**
507
+ * Creates a TypeScript index signature for dynamic property access.
508
+ * Defines the key type (default: `string`) and value type on an object.
509
+ */
510
+ function createIndexSignature(type, { modifiers, indexName = "key", indexType = factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) } = {}) {
511
+ return factory.createIndexSignature(modifiers, [createParameterSignature(indexName, { type: indexType })], type);
512
+ }
513
+ /**
514
+ * Creates a TypeScript type alias declaration with optional modifiers and type parameters.
515
+ */
516
+ function createTypeAliasDeclaration({ modifiers, name, typeParameters, type }) {
517
+ return factory.createTypeAliasDeclaration(modifiers, name, typeParameters, type);
518
+ }
519
+ /**
520
+ * Creates a TypeScript interface declaration with optional modifiers, type parameters, and members.
521
+ */
522
+ function createInterfaceDeclaration({ modifiers, name, typeParameters, members }) {
523
+ return factory.createInterfaceDeclaration(modifiers, name, typeParameters, void 0, members);
524
+ }
525
+ /**
526
+ * Creates a TypeScript type declaration as either a type alias or interface.
527
+ * Intelligently selects the syntax based on the type structure and attaches JSDoc comments.
528
+ */
529
+ function createTypeDeclaration({ syntax, isExportable, comments, name, type }) {
530
+ if (syntax === "interface" && ts.isTypeLiteralNode(type)) return appendJSDocToNode({
531
+ node: createInterfaceDeclaration({
532
+ members: [...type.members],
533
+ modifiers: isExportable ? [modifiers.export] : [],
534
+ name,
535
+ typeParameters: void 0
536
+ }),
537
+ comments
538
+ });
539
+ return appendJSDocToNode({
540
+ node: createTypeAliasDeclaration({
541
+ type,
542
+ modifiers: isExportable ? [modifiers.export] : [],
543
+ name,
544
+ typeParameters: void 0
545
+ }),
546
+ comments
547
+ });
548
+ }
549
+ /**
550
+ * Creates an import declaration with support for default imports, named imports, namespace imports, and type-only imports.
551
+ * Optionally rename imported members with `propertyName` and `name` pairs.
552
+ *
553
+ * @example Default import
554
+ * `import Pet from './Pet'`
555
+ *
556
+ * @example Named imports with rename
557
+ * `import { Pet as Cat } from './Pet'`
558
+ *
559
+ * @example Namespace import
560
+ * `import * as Pet from './Pet'`
561
+ */
562
+ function createImportDeclaration({ name, path, isTypeOnly = false, isNameSpace = false }) {
563
+ if (!Array.isArray(name)) {
564
+ const importPropertyName = isNameSpace ? void 0 : factory.createIdentifier(name);
565
+ const importName = isNameSpace ? factory.createNamespaceImport(factory.createIdentifier(name)) : void 0;
566
+ return factory.createImportDeclaration(void 0, factory.createImportClause(isTypeOnly, importPropertyName, importName), factory.createStringLiteral(path), void 0);
567
+ }
568
+ const sortedName = name.toSorted((a, b) => compareStrings(typeof a === "object" ? a.propertyName : a, typeof b === "object" ? b.propertyName : b));
569
+ return factory.createImportDeclaration(void 0, factory.createImportClause(isTypeOnly, void 0, factory.createNamedImports(sortedName.map((item) => {
570
+ if (typeof item === "object") {
571
+ const obj = item;
572
+ if (obj.name) return factory.createImportSpecifier(false, factory.createIdentifier(obj.propertyName), factory.createIdentifier(obj.name));
573
+ return factory.createImportSpecifier(false, void 0, factory.createIdentifier(obj.propertyName));
574
+ }
575
+ return factory.createImportSpecifier(false, void 0, factory.createIdentifier(item));
576
+ }))), factory.createStringLiteral(path), void 0);
577
+ }
578
+ /**
579
+ * Creates an export declaration with support for named exports, namespace exports, and type-only exports.
580
+ * Sorts export names alphabetically for consistent output across platforms.
581
+ */
582
+ function createExportDeclaration({ path, asAlias, isTypeOnly = false, name }) {
583
+ if (name && !Array.isArray(name) && !asAlias) console.warn(`When using name as string, asAlias should be true ${name}`);
584
+ if (!Array.isArray(name)) {
585
+ const parsedName = name?.match(/^\d/) ? `_${name?.slice(1)}` : name;
586
+ return factory.createExportDeclaration(void 0, isTypeOnly, asAlias && parsedName ? factory.createNamespaceExport(factory.createIdentifier(parsedName)) : void 0, factory.createStringLiteral(path), void 0);
587
+ }
588
+ const sortedName = name.toSorted((a, b) => compareStrings(typeof a === "string" ? a : a.text, typeof b === "string" ? b : b.text));
589
+ return factory.createExportDeclaration(void 0, isTypeOnly, factory.createNamedExports(sortedName.map((propertyName) => {
590
+ return factory.createExportSpecifier(false, void 0, typeof propertyName === "string" ? factory.createIdentifier(propertyName) : propertyName);
591
+ })), factory.createStringLiteral(path), void 0);
592
+ }
593
+ /**
594
+ * Apply casing transformation to enum keys
595
+ */
596
+ function applyEnumKeyCasing(key, casing = "none") {
597
+ if (casing === "none") return key;
598
+ if (casing === "screamingSnakeCase") return screamingSnakeCase(key);
599
+ if (casing === "snakeCase") return snakeCase(key);
600
+ if (casing === "pascalCase") return pascalCase(key);
601
+ if (casing === "camelCase") return camelCase(key);
602
+ return key;
603
+ }
604
+ /**
605
+ * Creates a TypeScript enum declaration or equivalent construct in various formats.
606
+ * Returns a tuple of [name node, type node] - name node may be undefined for certain types.
607
+ *
608
+ * @example
609
+ * ```ts
610
+ * const [name, type] = createEnumDeclaration({
611
+ * type: 'enum',
612
+ * name: 'petType',
613
+ * typeName: 'PetType',
614
+ * enums: [['cat', 'cat', 'A cat'], ['dog', 'dog']],
615
+ * })
616
+ * ```
617
+ */
618
+ function createEnumDeclaration({ type = "enum", name, typeName, enums, enumKeyCasing = "none" }) {
619
+ if (type === "literal" || type === "inlineLiteral") return [void 0, factory.createTypeAliasDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier(typeName), void 0, factory.createUnionTypeNode(enums.map(([_key, value]) => {
620
+ if (isNumber(value)) {
621
+ if (value < 0) return factory.createLiteralTypeNode(factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value))));
622
+ return factory.createLiteralTypeNode(factory.createNumericLiteral(value?.toString()));
623
+ }
624
+ if (typeof value === "boolean") return factory.createLiteralTypeNode(value ? factory.createTrue() : factory.createFalse());
625
+ if (value !== null && value !== void 0) return factory.createLiteralTypeNode(factory.createStringLiteral(value.toString()));
626
+ }).filter((node) => node !== void 0)))];
627
+ 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, description]) => {
628
+ let initializer = factory.createStringLiteral(value?.toString());
629
+ if (Number.parseInt(value.toString(), 10) === value && isNumber(value)) if (value < 0) initializer = factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)));
630
+ else initializer = factory.createNumericLiteral(value);
631
+ if (typeof value === "boolean") initializer = value ? factory.createTrue() : factory.createFalse();
632
+ if (isNumber(Number.parseInt(key.toString(), 10))) {
633
+ const casingKey = applyEnumKeyCasing(`${typeName}_${key}`, enumKeyCasing);
634
+ return appendJSDocToNode({
635
+ node: factory.createEnumMember(propertyName(casingKey), initializer),
636
+ comments: [description]
637
+ });
638
+ }
639
+ if (key !== null && key !== void 0) {
640
+ const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
641
+ return appendJSDocToNode({
642
+ node: factory.createEnumMember(propertyName(casingKey), initializer),
643
+ comments: [description]
644
+ });
645
+ }
646
+ }).filter((member) => member !== void 0))];
647
+ const identifierName = name;
648
+ if (enums.length === 0) return [void 0, factory.createTypeAliasDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier(typeName), void 0, factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword))];
649
+ return [factory.createVariableStatement([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createVariableDeclarationList([factory.createVariableDeclaration(factory.createIdentifier(identifierName), void 0, void 0, factory.createAsExpression(factory.createObjectLiteralExpression(enums.map(([key, value, description]) => {
650
+ let initializer = factory.createStringLiteral(value?.toString());
651
+ if (isNumber(value)) if (value < 0) initializer = factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, factory.createNumericLiteral(Math.abs(value)));
652
+ else initializer = factory.createNumericLiteral(value);
653
+ if (typeof value === "boolean") initializer = value ? factory.createTrue() : factory.createFalse();
654
+ if (key !== null && key !== void 0) {
655
+ const casingKey = applyEnumKeyCasing(key.toString(), enumKeyCasing);
656
+ return appendJSDocToNode({
657
+ node: factory.createPropertyAssignment(propertyName(casingKey), initializer),
658
+ comments: [description]
659
+ });
660
+ }
661
+ }).filter((property) => property !== void 0), true), factory.createTypeReferenceNode(factory.createIdentifier("const"), void 0)))], ts.NodeFlags.Const)), factory.createTypeAliasDeclaration([factory.createToken(ts.SyntaxKind.ExportKeyword)], factory.createIdentifier(typeName), void 0, factory.createIndexedAccessTypeNode(factory.createParenthesizedType(factory.createTypeQueryNode(factory.createIdentifier(identifierName), void 0)), factory.createTypeOperatorNode(ts.SyntaxKind.KeyOfKeyword, factory.createTypeQueryNode(factory.createIdentifier(identifierName), void 0))))];
662
+ }
663
+ /**
664
+ * Creates a TypeScript `Omit<T, Keys>` type reference node.
665
+ * Optionally wraps the type in `NonNullable<T>` if `nonNullable` is true.
666
+ */
667
+ function createOmitDeclaration({ keys, type, nonNullable }) {
668
+ const node = nonNullable ? factory.createTypeReferenceNode(factory.createIdentifier("NonNullable"), [type]) : type;
669
+ if (Array.isArray(keys)) return factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [node, factory.createUnionTypeNode(keys.map((key) => {
670
+ return factory.createLiteralTypeNode(factory.createStringLiteral(key));
671
+ }))]);
672
+ return factory.createTypeReferenceNode(factory.createIdentifier("Omit"), [node, factory.createLiteralTypeNode(factory.createStringLiteral(keys))]);
673
+ }
674
+ /**
675
+ * Pre-built TypeScript keyword type nodes for common primitive types.
676
+ * Use these to avoid repeatedly creating the same type nodes.
677
+ */
678
+ const keywordTypeNodes = {
679
+ any: factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
680
+ unknown: factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
681
+ void: factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword),
682
+ number: factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
683
+ integer: factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
684
+ bigint: factory.createKeywordTypeNode(ts.SyntaxKind.BigIntKeyword),
685
+ object: factory.createKeywordTypeNode(ts.SyntaxKind.ObjectKeyword),
686
+ string: factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
687
+ boolean: factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword),
688
+ undefined: factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword),
689
+ null: factory.createLiteralTypeNode(factory.createToken(ts.SyntaxKind.NullKeyword)),
690
+ never: factory.createKeywordTypeNode(ts.SyntaxKind.NeverKeyword)
691
+ };
692
+ /**
693
+ * Converts a path like '/pet/{petId}/uploadImage' to a template literal type
694
+ * like `/pet/${string}/uploadImage`
695
+ */
696
+ /**
697
+ * Converts an OAS-style path (e.g. `/pets/{petId}`) or an Express-style path
698
+ * (e.g. `/pets/:petId`) to a TypeScript template literal type
699
+ * like `` `/pets/${string}` ``.
700
+ */
701
+ function createUrlTemplateType(path) {
702
+ const normalized = path.replace(/:([^/]+)/g, "{$1}");
703
+ if (!normalized.includes("{")) return factory.createLiteralTypeNode(factory.createStringLiteral(normalized));
704
+ const segments = normalized.split(/(\{[^}]+\})/);
705
+ const parts = [];
706
+ const parameterIndices = [];
707
+ segments.forEach((segment) => {
708
+ if (segment.startsWith("{") && segment.endsWith("}")) {
709
+ parameterIndices.push(parts.length);
710
+ parts.push(segment);
711
+ } else if (segment) parts.push(segment);
712
+ });
713
+ const head = ts.factory.createTemplateHead(parts[0] || "");
714
+ const templateSpans = [];
715
+ parameterIndices.forEach((paramIndex, i) => {
716
+ const isLast = i === parameterIndices.length - 1;
717
+ const nextPart = parts[paramIndex + 1] || "";
718
+ const literal = isLast ? ts.factory.createTemplateTail(nextPart) : ts.factory.createTemplateMiddle(nextPart);
719
+ templateSpans.push(ts.factory.createTemplateLiteralTypeSpan(keywordTypeNodes.string, literal));
720
+ });
721
+ return ts.factory.createTemplateLiteralType(head, templateSpans);
722
+ }
723
+ /**
724
+ * Creates a TypeScript type literal node (anonymous object type).
725
+ */
726
+ const createTypeLiteralNode = factory.createTypeLiteralNode;
727
+ /**
728
+ * Creates a TypeScript type reference node (e.g., `Array<string>`, `Record<K, V>`).
729
+ */
730
+ const createTypeReferenceNode = factory.createTypeReferenceNode;
731
+ /**
732
+ * Creates a numeric literal type node.
733
+ */
734
+ const createNumericLiteral = factory.createNumericLiteral;
735
+ /**
736
+ * Creates a string literal type node.
737
+ */
738
+ const createStringLiteral = factory.createStringLiteral;
739
+ /**
740
+ * Creates an array type node (e.g., `T[]`).
741
+ */
742
+ const createArrayTypeNode = factory.createArrayTypeNode;
743
+ /**
744
+ * Creates a literal type node (e.g., `'hello'`, `42`, `true`).
745
+ */
746
+ const createLiteralTypeNode = factory.createLiteralTypeNode;
747
+ /**
748
+ * Creates an identifier node.
749
+ */
750
+ const createIdentifier = factory.createIdentifier;
751
+ /**
752
+ * Creates an optional type node (e.g., `T | undefined`).
753
+ */
754
+ const createOptionalTypeNode = factory.createOptionalTypeNode;
755
+ /**
756
+ * Creates a tuple type node (e.g., `[string, number]`).
757
+ */
758
+ const createTupleTypeNode = factory.createTupleTypeNode;
759
+ /**
760
+ * Creates a rest type node for variadic tuple elements (e.g., `...T[]`).
761
+ */
762
+ const createRestTypeNode = factory.createRestTypeNode;
763
+ /**
764
+ * Creates a boolean true literal type node.
765
+ */
766
+ const createTrue = factory.createTrue;
767
+ /**
768
+ * Creates a boolean false literal type node.
769
+ */
770
+ const createFalse = factory.createFalse;
771
+ /**
772
+ * Creates a prefix unary expression (e.g., negative numbers, logical not).
773
+ */
774
+ const createPrefixUnaryExpression = factory.createPrefixUnaryExpression;
775
+ /**
776
+ * Converts a primitive const value to a TypeScript literal type node.
777
+ * Handles negative numbers via a prefix unary expression.
778
+ */
779
+ function constToTypeNode(value, format) {
780
+ if (format === "boolean") return createLiteralTypeNode(value === true ? createTrue() : createFalse());
781
+ if (format === "number" && typeof value === "number") {
782
+ if (value < 0) return createLiteralTypeNode(createPrefixUnaryExpression(SyntaxKind.MinusToken, createNumericLiteral(Math.abs(value))));
783
+ return createLiteralTypeNode(createNumericLiteral(value));
784
+ }
785
+ return createLiteralTypeNode(createStringLiteral(String(value)));
786
+ }
787
+ /**
788
+ * Returns a `Date` reference type node when `representation` is `'date'`, otherwise falls back to `string`.
789
+ */
790
+ function dateOrStringNode(node) {
791
+ return node.representation === "date" ? createTypeReferenceNode(createIdentifier("Date")) : keywordTypeNodes.string;
792
+ }
793
+ /**
794
+ * Maps an array of `SchemaNode`s through the printer, filtering out `null` and `undefined` results.
795
+ */
796
+ function buildMemberNodes(members, print) {
797
+ return (members ?? []).map(print).filter(isNonNullable$1);
798
+ }
799
+ /**
800
+ * Builds a TypeScript tuple type node from an array schema's `items`,
801
+ * applying min/max slice and optional/rest element rules.
802
+ */
803
+ function buildTupleNode(node, print) {
804
+ let items = (node.items ?? []).map(print).filter(isNonNullable$1);
805
+ const restNode = node.rest ? print(node.rest) ?? void 0 : void 0;
806
+ const { min, max } = node;
807
+ if (max !== void 0) {
808
+ items = items.slice(0, max);
809
+ if (items.length < max && restNode) items = [...items, ...Array(max - items.length).fill(restNode)];
810
+ }
811
+ if (min !== void 0) items = items.map((item, i) => i >= min ? createOptionalTypeNode(item) : item);
812
+ if (max === void 0 && restNode) items.push(createRestTypeNode(createArrayTypeNode(restNode)));
813
+ return createTupleTypeNode(items);
814
+ }
815
+ /**
816
+ * Applies `nullable` and optional/nullish `| undefined` union modifiers to a property's resolved base type.
817
+ */
818
+ function buildPropertyType(schema, baseType, optionalType, optional) {
819
+ const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(optionalType);
820
+ const meta = syncSchemaRef(schema);
821
+ let type = baseType;
822
+ if (meta.nullable) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.null] });
823
+ if ((optional || meta.nullish || meta.optional) && addsUndefined) type = createUnionDeclaration({ nodes: [type, keywordTypeNodes.undefined] });
824
+ return type;
825
+ }
826
+ const indexSignaturePrinter = ts.createPrinter();
827
+ const indexSignatureSource = ts.createSourceFile("", "", ts.ScriptTarget.Latest);
828
+ /**
829
+ * Creates a TypeScript index signature for `additionalProperties` and `patternProperties` on an
830
+ * object schema node. TypeScript allows only one string index signature and its value must be
831
+ * assignable from every named property, so both keywords collapse into a single signature: the
832
+ * union of all value types (deduplicated), or `unknown` when the object also has fixed properties.
833
+ * The key regex of `patternProperties` cannot be expressed by an index signature and is dropped.
834
+ */
835
+ function buildIndexSignatures(node, propertyCount, print) {
836
+ const valueTypes = [];
837
+ if (node.additionalProperties && node.additionalProperties !== true) valueTypes.push(print(node.additionalProperties) ?? keywordTypeNodes.unknown);
838
+ else if (node.additionalProperties === true) valueTypes.push(keywordTypeNodes.unknown);
839
+ for (const schema of Object.values(node.patternProperties ?? {})) {
840
+ const patternType = print(schema) ?? keywordTypeNodes.unknown;
841
+ valueTypes.push(schema.nullable ? createUnionDeclaration({ nodes: [patternType, keywordTypeNodes.null] }) : patternType);
842
+ }
843
+ if (valueTypes.length === 0) return [];
844
+ const seen = /* @__PURE__ */ new Set();
845
+ const distinct = valueTypes.filter((type) => {
846
+ const key = indexSignaturePrinter.printNode(ts.EmitHint.Unspecified, type, indexSignatureSource);
847
+ return seen.has(key) ? false : (seen.add(key), true);
848
+ });
849
+ return [createIndexSignature(propertyCount > 0 ? keywordTypeNodes.unknown : createUnionDeclaration({ nodes: distinct }))];
850
+ }
851
+ //#endregion
852
+ //#region src/components/Enum.tsx
853
+ /**
854
+ * Resolves the runtime identifier name and the TypeScript type name for an enum schema node.
855
+ *
856
+ * The raw `node.name` may be a YAML key such as `"enumNames.Type"` which is not a
857
+ * valid TypeScript identifier. The resolver normalizes it. For inline enum properties the adapter
858
+ * already emits a PascalCase+suffix name, so resolution is typically a no-op.
859
+ *
860
+ * When `constCasing` is `'pascalCase'` and `typeSuffix` is empty, the const and the type
861
+ * resolve to the same name, which TypeScript merges into a single value+type declaration.
862
+ */
863
+ function getEnumNames({ node, enum: enumOptions, resolver }) {
864
+ const resolved = resolver.name(node.name);
865
+ return {
866
+ enumName: enumOptions.constCasing === "pascalCase" ? resolved : camelCase(node.name),
867
+ typeName: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) ? resolver.enum.keyName(node, enumOptions.typeSuffix) : resolved
868
+ };
869
+ }
870
+ /**
871
+ * Renders the enum declaration(s) for a single named `EnumSchemaNode`.
872
+ *
873
+ * Depending on `enum.type` this may emit:
874
+ * - A runtime object (`asConst`) plus a `typeof` type alias
875
+ * - A `const enum` or plain `enum` declaration (`constEnum` / `enum`)
876
+ * - A union literal type alias (`literal`)
877
+ *
878
+ * The emitted `File.Source` nodes carry the resolved names so that the barrel
879
+ * index picks up the correct export identifiers.
880
+ */
881
+ function Enum({ node, enum: enumOptions, resolver }) {
882
+ const { enumName, typeName } = getEnumNames({
883
+ node,
884
+ enum: enumOptions,
885
+ resolver
886
+ });
887
+ const [nameNode, typeNode] = createEnumDeclaration({
888
+ name: enumName,
889
+ typeName,
890
+ enums: node.namedEnumValues?.map((v) => [
891
+ trimQuotes(v.name.toString()),
892
+ v.value,
893
+ v.description
894
+ ]) ?? node.enumValues?.filter((v) => v !== null && v !== void 0).map((v) => [trimQuotes(v.toString()), v]) ?? [],
895
+ type: enumOptions.type,
896
+ enumKeyCasing: enumOptions.keyCasing
897
+ });
898
+ const namesMerge = !!nameNode && enumName === typeName;
899
+ return /* @__PURE__ */ jsxs(Fragment, { children: [nameNode && /* @__PURE__ */ jsx(File.Source, {
900
+ name: enumName,
901
+ isExportable: true,
902
+ isIndexable: true,
903
+ isTypeOnly: false,
904
+ children: parserTs().print(nameNode)
905
+ }), /* @__PURE__ */ jsx(File.Source, {
906
+ name: typeName,
907
+ isIndexable: !namesMerge,
908
+ isExportable: !namesMerge && ENUM_TYPES_WITH_RUNTIME_VALUE.has(enumOptions.type),
909
+ isTypeOnly: ENUM_TYPES_WITH_TYPE_ONLY.has(enumOptions.type),
910
+ children: parserTs().print(typeNode)
911
+ })] });
912
+ }
913
+ //#endregion
914
+ //#region ../../internals/shared/src/params.ts
915
+ /**
916
+ * Drops parameters that share the same name, keeping the first.
917
+ *
918
+ * A malformed spec can declare the same parameter name twice within one `in` location. Both would
919
+ * resolve to the same output property, so emitting both would yield an object type with a duplicate
920
+ * member, which TypeScript rejects. This is a defensive guard against that case, not a casing guard:
921
+ * parameter names flow through unchanged, so no two distinct names ever collide here anymore.
922
+ */
923
+ function dedupeParams(params) {
924
+ const seen = /* @__PURE__ */ new Set();
925
+ return params.filter((param) => {
926
+ if (seen.has(param.name)) return false;
927
+ seen.add(param.name);
928
+ return true;
929
+ });
930
+ }
931
+ //#endregion
932
+ //#region ../../internals/shared/src/operation.ts
933
+ /**
934
+ * Maps a content type to the PascalCase suffix used to name per-content-type variants
935
+ * (e.g. `application/json` → `Json`, `application/xml` → `Xml`, `multipart/form-data` → `FormData`).
936
+ */
937
+ function getContentTypeSuffix(contentType) {
938
+ const baseType = contentType.split(";")[0].trim();
939
+ if (baseType === "application/json") return "Json";
940
+ if (baseType === "multipart/form-data") return "FormData";
941
+ if (baseType === "application/x-www-form-urlencoded") return "FormUrlEncoded";
942
+ const parts = (baseType.split("/").pop() ?? baseType).split(/[^a-zA-Z0-9]+/).filter(Boolean);
943
+ if (parts.length === 0) return "Unknown";
944
+ return parts.map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
945
+ }
946
+ /**
947
+ * Appends a content-type suffix to a base name, keeping a trailing `Data` segment last
948
+ * (e.g. `AddPetData` + `Json` → `AddPetJsonData`, `AddPetStatus200` + `Xml` → `AddPetStatus200Xml`).
949
+ */
950
+ function getPerContentTypeName(baseName, suffix) {
951
+ if (baseName.endsWith("Data")) return suffix.endsWith("Data") ? baseName.slice(0, -4) + suffix : `${baseName.slice(0, -4)}${suffix}Data`;
952
+ return baseName + suffix;
953
+ }
954
+ /**
955
+ * Resolves per-content-type variant names for a set of content entries, deduplicating suffix
956
+ * collisions with a numeric counter. Entries without a schema are skipped. The returned `suffix` is
957
+ * the final (possibly counter-augmented) value, so callers can derive parallel names in another
958
+ * namespace (e.g. plugin-faker deriving the matching plugin-ts type name).
959
+ */
960
+ function resolveContentTypeVariants(entries, baseName) {
961
+ const usedNames = /* @__PURE__ */ new Set();
962
+ return entries.filter((entry) => entry.schema).map((entry) => {
963
+ const baseSuffix = getContentTypeSuffix(entry.contentType);
964
+ let suffix = baseSuffix;
965
+ let name = getPerContentTypeName(baseName, suffix);
966
+ let counter = 2;
967
+ while (usedNames.has(name)) {
968
+ suffix = `${baseSuffix}${counter++}`;
969
+ name = getPerContentTypeName(baseName, suffix);
970
+ }
971
+ usedNames.add(name);
972
+ return {
973
+ name,
974
+ suffix,
975
+ schema: entry.schema,
976
+ keysToOmit: entry.keysToOmit,
977
+ contentType: entry.contentType
978
+ };
979
+ });
980
+ }
981
+ function getOperationParameters(node) {
982
+ return {
983
+ path: dedupeParams(node.parameters.filter((param) => param.in === "path")),
984
+ query: dedupeParams(node.parameters.filter((param) => param.in === "query")),
985
+ header: dedupeParams(node.parameters.filter((param) => param.in === "header")),
986
+ cookie: dedupeParams(node.parameters.filter((param) => param.in === "cookie"))
987
+ };
988
+ }
989
+ /**
990
+ * Builds the combined `{ body, path, query, headers }` options object schema for an operation,
991
+ * referencing the already-resolved body and grouped param names. Shared by `@kubb/plugin-ts`'s
992
+ * `Options` type and `@kubb/plugin-zod`'s inferred options schema, so both printers emit the same
993
+ * shape from the same inputs. `primitive: 'object'` is a no-op for the TS printer and tells the Zod
994
+ * printer to emit `z.object(…)` rather than a record.
995
+ */
996
+ function buildOptionsSchema(node, resolver) {
997
+ const { path, query, header } = getOperationParameters(node);
998
+ const hasBody = Boolean(node.requestBody?.content?.[0]?.schema);
999
+ const createNever = () => ast.factory.createSchema({
1000
+ type: "never",
1001
+ primitive: void 0,
1002
+ optional: true
1003
+ });
1004
+ const groups = [
1005
+ {
1006
+ name: "path",
1007
+ params: path,
1008
+ resolve: resolver.param.path
1009
+ },
1010
+ {
1011
+ name: "query",
1012
+ params: query,
1013
+ resolve: resolver.param.query
1014
+ },
1015
+ {
1016
+ name: "headers",
1017
+ params: header,
1018
+ resolve: resolver.param.headers
1019
+ }
1020
+ ];
1021
+ return ast.factory.createSchema({
1022
+ type: "object",
1023
+ primitive: "object",
1024
+ deprecated: node.deprecated,
1025
+ properties: [ast.factory.createProperty({
1026
+ name: "body",
1027
+ required: hasBody,
1028
+ schema: hasBody ? ast.factory.createSchema({
1029
+ type: "ref",
1030
+ name: resolver.response.body(node)
1031
+ }) : createNever()
1032
+ }), ...groups.map(({ name, params, resolve }) => {
1033
+ const required = params.some((param) => param.required);
1034
+ return ast.factory.createProperty({
1035
+ name,
1036
+ required,
1037
+ schema: params.length > 0 ? ast.factory.createSchema({
1038
+ type: "ref",
1039
+ name: resolve.call(resolver.param, node, params[0]),
1040
+ optional: !required
1041
+ }) : createNever()
1042
+ });
1043
+ })]
1044
+ });
1045
+ }
1046
+ //#endregion
1047
+ //#region ../../internals/shared/src/resolver.ts
1048
+ /**
1049
+ * Resolves a single operation parameter name with the
1050
+ * `<operationId> <in> <name>` template.
1051
+ *
1052
+ * @example
1053
+ * `operationParamName.call(resolver, node, param) // → 'DeletePetPathPetId'`
1054
+ */
1055
+ function operationParamName(node, param) {
1056
+ return this.name(`${node.operationId} ${param.in} ${param.name}`);
1057
+ }
1058
+ /**
1059
+ * Builds the shared `param` namespace. Spread the result into `createResolver`
1060
+ * and override individual methods next to it when a plugin deviates.
1061
+ *
1062
+ * @example
1063
+ * ```ts
1064
+ * createResolver<PluginTs>({ param: createOperationParamResolver(), ... })
1065
+ * ```
1066
+ */
1067
+ function createOperationParamResolver() {
1068
+ return {
1069
+ name: operationParamName,
1070
+ path(node) {
1071
+ return this.name(`${node.operationId} Path`);
1072
+ },
1073
+ query(node) {
1074
+ return this.name(`${node.operationId} Query`);
1075
+ },
1076
+ headers(node) {
1077
+ return this.name(`${node.operationId} Headers`);
1078
+ }
1079
+ };
1080
+ }
1081
+ /**
1082
+ * Builds the shared `response` namespace. Spread the result into
1083
+ * `createResolver` and add plugin-specific methods (`options`, `error`) next
1084
+ * to it.
1085
+ *
1086
+ * @example
1087
+ * ```ts
1088
+ * createResolver<PluginTs>({ response: { ...createOperationResponseResolver(), options(node) {...} }, ... })
1089
+ * ```
1090
+ */
1091
+ function createOperationResponseResolver() {
1092
+ return {
1093
+ status(node, statusCode) {
1094
+ return this.name(`${node.operationId} Status ${statusCode}`);
1095
+ },
1096
+ body(node) {
1097
+ return this.name(`${node.operationId} Body`);
1098
+ },
1099
+ responses(node) {
1100
+ return this.name(`${node.operationId} Responses`);
1101
+ },
1102
+ response(node) {
1103
+ return this.name(`${node.operationId} Response`);
1104
+ }
1105
+ };
1106
+ }
1107
+ /**
1108
+ * Builds a resolver `file` override whose base name runs every path segment
1109
+ * through `toFilePath`, casing the final segment with `caseLast`.
1110
+ *
1111
+ * @example
1112
+ * ```ts
1113
+ * createResolver<PluginTs>({ file: createCasedFile(pascalCase), ... })
1114
+ * ```
1115
+ */
1116
+ function createCasedFile(caseLast) {
1117
+ return { baseName({ name, extname }) {
1118
+ return `${toFilePath(name, caseLast)}${extname}`;
1119
+ } };
1120
+ }
1121
+ //#endregion
1122
+ //#region ../../internals/shared/src/refs.ts
1123
+ /**
1124
+ * Collects the resolved target name of every pointer-carrying ref in a schema tree, in
1125
+ * first-occurrence order. Use this for name-only checks (e.g. redeclaration detection) where
1126
+ * `resolver.imports` would resolve file paths that are then discarded.
1127
+ */
1128
+ function collectRefNames(schema) {
1129
+ return ast.collectSync(schema, { schema: (node) => {
1130
+ const refNode = ast.narrowSchema(node, "ref");
1131
+ if (!refNode?.ref) return null;
1132
+ return ast.resolveRefName(refNode);
1133
+ } });
1134
+ }
1135
+ //#endregion
1136
+ //#region ../../internals/shared/src/group.ts
1137
+ /**
1138
+ * Builds the `group` config a Kubb plugin passes to `ctx.setOptions`, applying the
1139
+ * shared default naming so every plugin groups output consistently:
1140
+ *
1141
+ * - `path` groups use the second path segment (`/pet/findByStatus` → `pet`).
1142
+ * - other groups use the camelCased group (`pet store` → `petStore`).
1143
+ *
1144
+ * A user-provided `group.name` always wins over the default namer, so callers stay in
1145
+ * control of their output folders. Returns `null` when grouping is disabled, matching the
1146
+ * per-plugin convention.
1147
+ *
1148
+ * @param group - The user-supplied group option, or `undefined` to disable grouping.
1149
+ *
1150
+ * @example
1151
+ * ```ts
1152
+ * createGroupConfig(group) // shared across every plugin
1153
+ * ```
1154
+ */
1155
+ function createGroupConfig(group) {
1156
+ if (!group) return null;
1157
+ const defaultName = (ctx) => {
1158
+ if (group.type === "path") return `${ctx.group.split("/")[1]}`;
1159
+ return camelCase(ctx.group);
1160
+ };
1161
+ return {
1162
+ ...group,
1163
+ name: group.name ? group.name : defaultName
1164
+ };
1165
+ }
1166
+ //#endregion
1167
+ //#region ../../internals/shared/src/schemaTraversal.ts
1168
+ /**
1169
+ * Maps each property of an object schema to its transformed output. Pairs every result with the
1170
+ * original property so the printer keeps full control over modifiers, getters, and key syntax.
1171
+ *
1172
+ * @example
1173
+ * ```ts
1174
+ * const entries = mapSchemaProperties(node, (schema) => this.transform(schema))
1175
+ * // entries: [{ name: 'id', property, output: 'z.number()' }, ...]
1176
+ * ```
1177
+ */
1178
+ function mapSchemaProperties(node, transform) {
1179
+ return node.properties.map((property) => ({
1180
+ name: property.name,
1181
+ property,
1182
+ output: transform(property.schema)
1183
+ }));
1184
+ }
1185
+ /**
1186
+ * Maps each item of an array or tuple schema to its transformed output, pairing every result with
1187
+ * the original item.
1188
+ */
1189
+ function mapSchemaItems(node, transform) {
1190
+ return (node.items ?? []).map((schema) => ({
1191
+ schema,
1192
+ output: transform(schema)
1193
+ }));
1194
+ }
1195
+ //#endregion
1196
+ //#region src/utils.ts
1197
+ /**
1198
+ * Tells whether a `const` (single-value enum) should render as a bare literal type (`'active'`)
1199
+ * rather than a named enum reference or a runtime enum declaration.
1200
+ *
1201
+ * The parser folds `const` into a single-value enum node. The adapter decides which schemas are
1202
+ * named enums and lists them in `enumSchemaNames`, and references to those names get suffixed (for
1203
+ * example `StatusKey`). A const renders as a literal only when the adapter has not registered it as
1204
+ * a named enum, which keeps the declaration and its references in sync across adapter versions.
1205
+ */
1206
+ function isInlineConstEnum(node, enumSchemaNames) {
1207
+ return (node.namedEnumValues ?? node.enumValues ?? []).length === 1 && !(node.name && enumSchemaNames?.has(node.name));
1208
+ }
1209
+ /**
1210
+ * Collects JSDoc annotation strings for a schema node.
1211
+ *
1212
+ * Only uses official JSDoc tags from https://jsdoc.app/: `@description`, `@deprecated`, `@default`, `@example`, `@type`.
1213
+ * Constraint metadata (min/max length, pattern, multipleOf, min/maxProperties) is emitted as plain-text lines.
1214
+
1215
+ */
1216
+ function isSchemaOptional(schema) {
1217
+ return Boolean("optional" in schema && schema.optional || "nullish" in schema && schema.nullish);
1218
+ }
1219
+ function formatExample(value) {
1220
+ if (value === null || typeof value !== "object") return String(value);
1221
+ return (JSON.stringify(value) ?? String(value)).replaceAll("*/", "*\\/");
1222
+ }
1223
+ function buildPropertyJSDocComments(schema, optional) {
1224
+ const meta = syncSchemaRef(schema);
1225
+ const isArray = meta?.primitive === "array";
1226
+ const hasDescription = meta && "description" in meta && meta.description;
1227
+ const formatComment = meta && "format" in meta && meta.format ? hasDescription ? [" ", `Format: \`${meta.format}\``] : ["@description", `Format: \`${meta.format}\``] : [];
1228
+ const exampleValues = meta?.examples ?? [];
1229
+ const comments = [
1230
+ hasDescription ? `@description ${jsStringEscape(meta.description)}` : null,
1231
+ ...formatComment,
1232
+ meta && "deprecated" in meta && meta.deprecated ? "@deprecated" : null,
1233
+ !isArray && meta && "min" in meta && meta.min !== void 0 ? `@minLength ${meta.min}` : null,
1234
+ !isArray && meta && "max" in meta && meta.max !== void 0 ? `@maxLength ${meta.max}` : null,
1235
+ meta && "pattern" in meta && meta.pattern ? `@pattern ${meta.pattern}` : null,
1236
+ meta && "default" in meta && meta.default !== void 0 ? `@default ${"primitive" in meta && meta.primitive === "string" && typeof meta.default === "string" ? stringify(meta.default) : meta.default}` : null,
1237
+ ...exampleValues.map((example) => `@example ${formatExample(example)}`)
1238
+ ].filter(Boolean);
1239
+ const typeTag = comments.length && meta && "primitive" in meta && meta.primitive ? [`@type ${meta.primitive}`, optional ?? isSchemaOptional(schema) ? " | undefined" : null].filter(Boolean).join("") : null;
1240
+ return [...comments, typeTag].filter(Boolean);
1241
+ }
1242
+ /**
1243
+ * Builds the object schema for a group of parameters sharing one `in` location (path, query, or
1244
+ * header), embedding each param's own schema (and JSDoc) directly rather than referencing a
1245
+ * separate per-param type — the group itself is the only type these params get exported as.
1246
+ */
1247
+ function buildParams({ params }) {
1248
+ return ast.factory.createSchema({
1249
+ type: "object",
1250
+ properties: params.map((param) => ast.factory.createProperty({
1251
+ name: param.name,
1252
+ required: param.required,
1253
+ schema: ast.factory.createSchema({
1254
+ ...param.schema,
1255
+ optional: !param.required
1256
+ })
1257
+ }))
1258
+ });
1259
+ }
1260
+ /**
1261
+ * The schema a status occupies in the `<Name>Responses` record. A status that documents several
1262
+ * content types becomes a `{ contentType; data }` union so the runtime can surface the negotiated type
1263
+ * on `result.parsed`, while the standalone `<Name>StatusNNN` alias stays the plain body union that the
1264
+ * query hooks and `result.data` use.
1265
+ */
1266
+ function buildResponseRecordEntry(node, res, resolver) {
1267
+ const statusName = resolver.response.status(node, res.statusCode);
1268
+ const variants = (res.content ?? []).filter((entry) => entry.schema);
1269
+ if (variants.length <= 1) return ast.factory.createSchema({
1270
+ type: "ref",
1271
+ name: statusName
1272
+ });
1273
+ return ast.factory.createSchema({
1274
+ type: "union",
1275
+ members: resolveContentTypeVariants(variants, statusName).map((variant) => ast.factory.createSchema({
1276
+ type: "object",
1277
+ properties: [ast.factory.createProperty({
1278
+ name: "contentType",
1279
+ required: true,
1280
+ schema: ast.factory.createSchema({
1281
+ type: "enum",
1282
+ enumValues: [variant.contentType]
1283
+ })
1284
+ }), ast.factory.createProperty({
1285
+ name: "data",
1286
+ required: true,
1287
+ schema: ast.factory.createSchema({
1288
+ type: "ref",
1289
+ name: variant.name
1290
+ })
1291
+ })]
1292
+ }))
1293
+ });
1294
+ }
1295
+ function buildResponses(node, { resolver }) {
1296
+ return ast.factory.createSchema({
1297
+ type: "object",
1298
+ properties: node.responses.map((res) => ast.factory.createProperty({
1299
+ name: String(res.statusCode),
1300
+ required: true,
1301
+ schema: buildResponseRecordEntry(node, res, resolver)
1302
+ }))
1303
+ });
1304
+ }
1305
+ function buildResponseUnion(node, { resolver }) {
1306
+ const responsesWithSchema = node.responses.filter((res) => res.content?.some((entry) => entry.schema));
1307
+ if (responsesWithSchema.length === 0) return null;
1308
+ return ast.factory.createSchema({
1309
+ type: "union",
1310
+ members: responsesWithSchema.map((res) => ast.factory.createSchema({
1311
+ type: "ref",
1312
+ name: resolver.response.status(node, res.statusCode)
1313
+ }))
1314
+ });
1315
+ }
1316
+ //#endregion
1317
+ //#region src/components/Type.tsx
1318
+ function Type({ name, node, printer, enum: enumOptions, resolver }) {
1319
+ const enumSchemaNodes = ast.collectSync(node, { schema(n) {
1320
+ const enumNode = ast.narrowSchema(n, ast.schemaTypes.enum);
1321
+ if (enumNode?.name && !isInlineConstEnum(enumNode, printer.options.enumSchemaNames)) return enumNode;
1322
+ } });
1323
+ const output = printer.print(node);
1324
+ if (!output) return;
1325
+ const enums = [...new Map(enumSchemaNodes.map((n) => [n.name, n])).values()].map((node) => {
1326
+ return {
1327
+ node,
1328
+ ...getEnumNames({
1329
+ node,
1330
+ enum: enumOptions,
1331
+ resolver
1332
+ })
1333
+ };
1334
+ });
1335
+ const shouldExportEnums = enumOptions.type !== "inlineLiteral";
1336
+ const shouldExportType = enumOptions.type === "inlineLiteral" || enums.every((item) => item.typeName !== name);
1337
+ return /* @__PURE__ */ jsxs(Fragment, { children: [shouldExportEnums && enums.map(({ node }) => /* @__PURE__ */ jsx(Enum, {
1338
+ node,
1339
+ enum: enumOptions,
1340
+ resolver
1341
+ }, node.name)), shouldExportType && /* @__PURE__ */ jsx(File.Source, {
1342
+ name,
1343
+ isTypeOnly: true,
1344
+ isExportable: true,
1345
+ isIndexable: true,
1346
+ children: output
1347
+ })] });
1348
+ }
1349
+ //#endregion
1350
+ //#region src/printers/printerTs.ts
1351
+ const { isNonNullable } = factory_exports;
1352
+ /**
1353
+ * TypeScript type printer built with `definePrinter`.
1354
+ *
1355
+ * Converts a `SchemaNode` AST node into a TypeScript AST node:
1356
+ * - **`printer.print(node)`** — when `options.typeName` is set, returns a full
1357
+ * `type Name = …` or `interface Name { … }` declaration (`ts.Node`).
1358
+ * Without `typeName`, returns the raw `ts.TypeNode` for the schema.
1359
+ *
1360
+ * Dispatches on `node.type` to the appropriate handler in `nodes`. Options are closed
1361
+ * over per printer instance, so each call to `printerTs(options)` produces an independent printer.
1362
+ *
1363
+ * @example Raw type node (no `typeName`)
1364
+ * ```ts
1365
+ * const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enum: { type: 'inlineLiteral' } })
1366
+ * const typeNode = printer.print(schemaNode) // ts.TypeNode
1367
+ * ```
1368
+ *
1369
+ * @example Full declaration (with `typeName`)
1370
+ * ```ts
1371
+ * const printer = printerTs({ optionalType: 'questionToken', arrayType: 'array', enum: { type: 'inlineLiteral' }, typeName: 'MyType' })
1372
+ * const declaration = printer.print(schemaNode) // ts.TypeAliasDeclaration | ts.InterfaceDeclaration
1373
+ * ```
1374
+ */
1375
+ const printerTs = ast.createPrinter((options) => {
1376
+ const addsUndefined = OPTIONAL_ADDS_UNDEFINED.has(options.optionalType);
1377
+ return {
1378
+ name: "typescript",
1379
+ options,
1380
+ nodes: {
1381
+ any: () => keywordTypeNodes.any,
1382
+ unknown: () => keywordTypeNodes.unknown,
1383
+ void: () => keywordTypeNodes.void,
1384
+ never: () => keywordTypeNodes.never,
1385
+ boolean: () => keywordTypeNodes.boolean,
1386
+ null: () => keywordTypeNodes.null,
1387
+ blob: () => createTypeReferenceNode("Blob", []),
1388
+ string: () => keywordTypeNodes.string,
1389
+ uuid: () => keywordTypeNodes.string,
1390
+ email: () => keywordTypeNodes.string,
1391
+ url: (node) => {
1392
+ if (node.path) return createUrlTemplateType(node.path);
1393
+ return keywordTypeNodes.string;
1394
+ },
1395
+ ipv4: () => keywordTypeNodes.string,
1396
+ ipv6: () => keywordTypeNodes.string,
1397
+ datetime: () => keywordTypeNodes.string,
1398
+ number: () => keywordTypeNodes.number,
1399
+ integer: () => keywordTypeNodes.number,
1400
+ bigint: () => keywordTypeNodes.bigint,
1401
+ date: dateOrStringNode,
1402
+ time: dateOrStringNode,
1403
+ ref(node) {
1404
+ if (!node.name) return null;
1405
+ const refName = ast.resolveRefName(node);
1406
+ if (!refName) return null;
1407
+ const name = node.ref && ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enum.type) && this.options.enum.typeSuffix && this.options.enumSchemaNames?.has(refName) ? this.options.resolver.enum.keyName({ name: refName }, this.options.enum.typeSuffix) : node.ref ? this.options.resolver.name(refName) : refName;
1408
+ return createTypeReferenceNode(name, void 0);
1409
+ },
1410
+ enum(node) {
1411
+ const values = node.namedEnumValues?.map((v) => v.value) ?? node.enumValues ?? [];
1412
+ if (this.options.enum.type === "inlineLiteral" || !node.name || isInlineConstEnum(node, this.options.enumSchemaNames)) return createUnionDeclaration({
1413
+ withParentheses: true,
1414
+ nodes: values.filter((v) => v !== null && v !== void 0).map((value) => constToTypeNode(value, typeof value)).filter(isNonNullable)
1415
+ }) ?? void 0;
1416
+ const resolvedName = ENUM_TYPES_WITH_KEY_SUFFIX.has(this.options.enum.type) && this.options.enum.typeSuffix ? this.options.resolver.enum.keyName(node, this.options.enum.typeSuffix) : this.options.resolver.name(node.name);
1417
+ return createTypeReferenceNode(resolvedName, void 0);
1418
+ },
1419
+ union(node) {
1420
+ const members = node.members ?? [];
1421
+ const hasStringLiteral = members.some((m) => {
1422
+ return ast.narrowSchema(m, ast.schemaTypes.enum)?.primitive === "string";
1423
+ });
1424
+ const hasPlainString = members.some((m) => isStringType(m));
1425
+ if (hasStringLiteral && hasPlainString) return createUnionDeclaration({
1426
+ withParentheses: true,
1427
+ nodes: members.map((m) => {
1428
+ if (isStringType(m)) return createIntersectionDeclaration({
1429
+ nodes: [keywordTypeNodes.string, createTypeLiteralNode([])],
1430
+ withParentheses: true
1431
+ });
1432
+ return this.transform(m);
1433
+ }).filter(isNonNullable)
1434
+ }) ?? void 0;
1435
+ return createUnionDeclaration({
1436
+ withParentheses: true,
1437
+ nodes: buildMemberNodes(members, this.transform)
1438
+ }) ?? void 0;
1439
+ },
1440
+ intersection(node) {
1441
+ return createIntersectionDeclaration({
1442
+ withParentheses: true,
1443
+ nodes: buildMemberNodes(node.members, this.transform)
1444
+ }) ?? null;
1445
+ },
1446
+ array(node) {
1447
+ return createArrayDeclaration({
1448
+ nodes: mapSchemaItems(node, (item) => this.transform(item)).map(({ output }) => output).filter(isNonNullable),
1449
+ arrayType: this.options.arrayType
1450
+ }) ?? null;
1451
+ },
1452
+ tuple(node) {
1453
+ return buildTupleNode(node, this.transform) ?? null;
1454
+ },
1455
+ object(node) {
1456
+ const { transform, options } = this;
1457
+ const addsQuestionToken = OPTIONAL_ADDS_QUESTION_TOKEN.has(options.optionalType);
1458
+ const propertyNodes = mapSchemaProperties(node, (schema) => transform(schema)).map(({ name, property, output }) => {
1459
+ const baseType = output ?? keywordTypeNodes.unknown;
1460
+ const optional = !property.required || !!property.schema.optional || !!property.schema.nullish;
1461
+ const type = buildPropertyType(property.schema, baseType, options.optionalType, optional);
1462
+ const propMeta = syncSchemaRef(property.schema);
1463
+ return appendJSDocToNode({
1464
+ node: createPropertySignature({
1465
+ questionToken: optional ? addsQuestionToken : false,
1466
+ name,
1467
+ type,
1468
+ readOnly: propMeta?.readOnly
1469
+ }),
1470
+ comments: buildPropertyJSDocComments(property.schema, optional)
1471
+ });
1472
+ });
1473
+ const allElements = [...propertyNodes, ...buildIndexSignatures(node, propertyNodes.length, transform)];
1474
+ if (!allElements.length) return keywordTypeNodes.object;
1475
+ return createTypeLiteralNode(allElements);
1476
+ },
1477
+ ...options.nodes
1478
+ },
1479
+ print(node) {
1480
+ const { name, syntaxType = "type", description, keysToOmit } = this.options;
1481
+ const transformed = this.transform(node);
1482
+ if (!transformed) return null;
1483
+ const meta = syncSchemaRef(node);
1484
+ if (!name) {
1485
+ const withNullable = meta.nullable ? createUnionDeclaration({ nodes: [transformed, keywordTypeNodes.null] }) : transformed;
1486
+ const result = (meta.nullish || meta.optional) && addsUndefined ? createUnionDeclaration({ nodes: [withNullable, keywordTypeNodes.undefined] }) : withNullable;
1487
+ return parserTs().print(result);
1488
+ }
1489
+ const inner = (() => {
1490
+ const omitted = keysToOmit?.length ? createOmitDeclaration({
1491
+ keys: keysToOmit,
1492
+ type: transformed,
1493
+ nonNullable: true
1494
+ }) : transformed;
1495
+ const withNullable = meta.nullable ? createUnionDeclaration({ nodes: [omitted, keywordTypeNodes.null] }) : omitted;
1496
+ return meta.nullish || meta.optional ? createUnionDeclaration({ nodes: [withNullable, keywordTypeNodes.undefined] }) : withNullable;
1497
+ })();
1498
+ const typeNode = createTypeDeclaration({
1499
+ name,
1500
+ isExportable: true,
1501
+ type: inner,
1502
+ syntax: syntaxType === "type" || inner.kind === syntaxKind.union || !!keysToOmit?.length ? "type" : "interface",
1503
+ comments: buildPropertyJSDocComments({
1504
+ ...meta,
1505
+ description
1506
+ })
1507
+ });
1508
+ return parserTs().print(typeNode);
1509
+ }
1510
+ };
1511
+ });
1512
+ //#endregion
1513
+ //#region src/generators/typeGenerator.tsx
1514
+ /**
1515
+ * Resolves the imported type name for a referenced schema. An enum schema emitted
1516
+ * as a `const` object imports its suffixed key alias (e.g. `StatusKey`); every other
1517
+ * schema imports its plain resolved name.
1518
+ */
1519
+ function resolveImportName({ schemaName, enumOptions, enumSchemaNames, resolver }) {
1520
+ if (ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && enumOptions.typeSuffix && enumSchemaNames.has(schemaName)) return resolver.enum.keyName({ name: schemaName }, enumOptions.typeSuffix);
1521
+ return resolver.name(schemaName);
1522
+ }
1523
+ /**
1524
+ * Built-in generator for `@kubb/plugin-ts`. Emits one TypeScript file per
1525
+ * schema in the spec plus per-operation request, response, and parameter
1526
+ * types. Drop-replace with a custom `Generator<PluginTs>` to change how
1527
+ * TypeScript output is produced.
1528
+ */
1529
+ const typeGenerator = defineGenerator({
1530
+ name: "typescript",
1531
+ renderer: jsxRenderer,
1532
+ schema(node, ctx) {
1533
+ const { enum: enumOptions, syntaxType, optionalType, arrayType, output, group, printer } = ctx.options;
1534
+ const { config, resolver, root } = ctx;
1535
+ if (!node.name) return;
1536
+ const enumSchemaNames = new Set(ctx.meta.enumNames);
1537
+ const importName = (schemaName) => resolveImportName({
1538
+ schemaName,
1539
+ enumOptions,
1540
+ enumSchemaNames,
1541
+ resolver
1542
+ });
1543
+ const imports = resolver.imports({
1544
+ node,
1545
+ root,
1546
+ output,
1547
+ group: group ?? void 0,
1548
+ name: importName
1549
+ });
1550
+ const enumNode = ast.narrowSchema(node, ast.schemaTypes.enum);
1551
+ const isEnumSchema = !!enumNode && !isInlineConstEnum(enumNode, enumSchemaNames);
1552
+ const meta = {
1553
+ name: ENUM_TYPES_WITH_KEY_SUFFIX.has(enumOptions.type) && isEnumSchema ? resolver.enum.keyName(node, enumOptions.typeSuffix) : resolver.name(node.name),
1554
+ file: resolver.file({
1555
+ name: node.name,
1556
+ extname: ".ts",
1557
+ root,
1558
+ output,
1559
+ group: group ?? void 0
1560
+ })
1561
+ };
1562
+ const schemaPrinter = printerTs({
1563
+ optionalType,
1564
+ arrayType,
1565
+ enum: enumOptions,
1566
+ name: meta.name,
1567
+ syntaxType,
1568
+ description: node.description,
1569
+ resolver,
1570
+ enumSchemaNames,
1571
+ nodes: printer?.nodes
1572
+ });
1573
+ return /* @__PURE__ */ jsxs(File, {
1574
+ baseName: meta.file.baseName,
1575
+ path: meta.file.path,
1576
+ meta: meta.file.meta,
1577
+ banner: resolver.default.banner(ctx.meta, {
1578
+ output,
1579
+ config,
1580
+ file: {
1581
+ path: meta.file.path,
1582
+ baseName: meta.file.baseName
1583
+ }
1584
+ }),
1585
+ footer: resolver.default.footer(ctx.meta, {
1586
+ output,
1587
+ config,
1588
+ file: {
1589
+ path: meta.file.path,
1590
+ baseName: meta.file.baseName
1591
+ }
1592
+ }),
1593
+ children: [imports.map((imp) => /* @__PURE__ */ jsx(File.Import, {
1594
+ root: meta.file.path,
1595
+ path: imp.path,
1596
+ name: imp.name,
1597
+ isTypeOnly: true
1598
+ }, [
1599
+ node.name,
1600
+ imp.path,
1601
+ imp.isTypeOnly
1602
+ ].join("-"))), /* @__PURE__ */ jsx(Type, {
1603
+ name: meta.name,
1604
+ node,
1605
+ enum: enumOptions,
1606
+ resolver,
1607
+ printer: schemaPrinter
1608
+ })]
1609
+ });
1610
+ },
1611
+ operation(node, ctx) {
1612
+ const { enum: enumOptions, optionalType, arrayType, syntaxType, group, output, printer } = ctx.options;
1613
+ const { config, resolver, root } = ctx;
1614
+ const meta = { file: resolver.file({
1615
+ name: node.operationId,
1616
+ extname: ".ts",
1617
+ tag: node.tags[0] ?? "default",
1618
+ path: node.path,
1619
+ root,
1620
+ output,
1621
+ group: group ?? void 0
1622
+ }) };
1623
+ const enumSchemaNames = new Set(ctx.meta.enumNames);
1624
+ const importName = (schemaName) => resolveImportName({
1625
+ schemaName,
1626
+ enumOptions,
1627
+ enumSchemaNames,
1628
+ resolver
1629
+ });
1630
+ function renderSchemaType({ schema, name, keysToOmit }) {
1631
+ if (!schema) return null;
1632
+ const imports = resolver.imports({
1633
+ node: schema,
1634
+ root,
1635
+ output,
1636
+ group: group ?? void 0,
1637
+ name: importName
1638
+ });
1639
+ const schemaPrinter = printerTs({
1640
+ optionalType,
1641
+ arrayType,
1642
+ enum: enumOptions,
1643
+ name,
1644
+ syntaxType,
1645
+ description: schema.description,
1646
+ keysToOmit,
1647
+ resolver,
1648
+ enumSchemaNames,
1649
+ nodes: printer?.nodes
1650
+ });
1651
+ return /* @__PURE__ */ jsxs(Fragment, { children: [imports.map((imp) => /* @__PURE__ */ jsx(File.Import, {
1652
+ root: meta.file.path,
1653
+ path: imp.path,
1654
+ name: imp.name,
1655
+ isTypeOnly: true
1656
+ }, [
1657
+ name,
1658
+ imp.path,
1659
+ imp.isTypeOnly
1660
+ ].join("-"))), /* @__PURE__ */ jsx(Type, {
1661
+ name,
1662
+ node: schema,
1663
+ enum: enumOptions,
1664
+ resolver,
1665
+ printer: schemaPrinter
1666
+ })] });
1667
+ }
1668
+ /**
1669
+ * Emits an individual type per content type plus a plain union alias under `baseName`. Shared by
1670
+ * the request body and multi-content-type responses. The response record discriminates these
1671
+ * variants by content type in {@link buildResponses}; the standalone alias stays a plain union so
1672
+ * query hooks and `result.data` keep the bare body.
1673
+ */
1674
+ function buildContentTypeVariants(entries, baseName, decorate) {
1675
+ const variants = resolveContentTypeVariants(entries, baseName);
1676
+ const unionSchema = ast.factory.createSchema({
1677
+ type: "union",
1678
+ members: variants.map((variant) => ast.factory.createSchema({
1679
+ type: "ref",
1680
+ name: variant.name
1681
+ }))
1682
+ });
1683
+ return /* @__PURE__ */ jsxs(Fragment, { children: [variants.map((variant) => renderSchemaType({
1684
+ schema: decorate ? decorate(variant.schema) : variant.schema,
1685
+ name: variant.name,
1686
+ keysToOmit: variant.keysToOmit
1687
+ })), renderSchemaType({
1688
+ schema: unionSchema,
1689
+ name: baseName
1690
+ })] });
1691
+ }
1692
+ const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
1693
+ const paramGroupTypes = [
1694
+ pathParams.length > 0 && renderSchemaType({
1695
+ schema: buildParams({ params: pathParams }),
1696
+ name: resolver.param.path(node, pathParams[0])
1697
+ }),
1698
+ queryParams.length > 0 && renderSchemaType({
1699
+ schema: buildParams({ params: queryParams }),
1700
+ name: resolver.param.query(node, queryParams[0])
1701
+ }),
1702
+ headerParams.length > 0 && renderSchemaType({
1703
+ schema: buildParams({ params: headerParams }),
1704
+ name: resolver.param.headers(node, headerParams[0])
1705
+ })
1706
+ ];
1707
+ const requestBodyContent = node.requestBody?.content ?? [];
1708
+ function buildRequestType() {
1709
+ if (requestBodyContent.length === 0) return null;
1710
+ if (requestBodyContent.length === 1) {
1711
+ const entry = requestBodyContent[0];
1712
+ if (!entry.schema) return null;
1713
+ return renderSchemaType({
1714
+ schema: {
1715
+ ...entry.schema,
1716
+ description: node.requestBody.description ?? entry.schema.description
1717
+ },
1718
+ name: resolver.response.body(node),
1719
+ keysToOmit: entry.keysToOmit
1720
+ });
1721
+ }
1722
+ return buildContentTypeVariants(requestBodyContent, resolver.response.body(node), (schema) => ({
1723
+ ...schema,
1724
+ description: node.requestBody.description ?? schema.description
1725
+ }));
1726
+ }
1727
+ const requestType = buildRequestType();
1728
+ const responseTypes = node.responses.map((res) => {
1729
+ const variants = (res.content ?? []).filter((entry) => entry.schema);
1730
+ if (variants.length > 1) return buildContentTypeVariants(variants, resolver.response.status(node, res.statusCode));
1731
+ const primary = variants[0] ?? res.content?.[0];
1732
+ return renderSchemaType({
1733
+ schema: primary?.schema ?? null,
1734
+ name: resolver.response.status(node, res.statusCode),
1735
+ keysToOmit: primary?.keysToOmit
1736
+ });
1737
+ });
1738
+ const optionsType = renderSchemaType({
1739
+ schema: buildOptionsSchema(node, resolver),
1740
+ name: resolver.response.options(node)
1741
+ });
1742
+ const responsesType = renderSchemaType({
1743
+ schema: buildResponses(node, { resolver }),
1744
+ name: resolver.response.responses(node)
1745
+ });
1746
+ function buildResponseType() {
1747
+ const hasSchema = (res) => (res.content ?? []).some((entry) => entry.schema);
1748
+ if (!node.responses.some(hasSchema)) return null;
1749
+ const responseName = resolver.response.response(node);
1750
+ const responsesWithSchema = node.responses.filter(hasSchema);
1751
+ if (new Set(responsesWithSchema.flatMap((res) => (res.content ?? []).flatMap((entry) => entry.schema ? collectRefNames(entry.schema).map(importName) : []))).has(responseName)) return null;
1752
+ return renderSchemaType({
1753
+ schema: {
1754
+ ...buildResponseUnion(node, { resolver }),
1755
+ description: "Union of all possible responses"
1756
+ },
1757
+ name: responseName
1758
+ });
1759
+ }
1760
+ const responseType = buildResponseType();
1761
+ return /* @__PURE__ */ jsxs(File, {
1762
+ baseName: meta.file.baseName,
1763
+ path: meta.file.path,
1764
+ meta: meta.file.meta,
1765
+ banner: resolver.default.banner(ctx.meta, {
1766
+ output,
1767
+ config,
1768
+ file: {
1769
+ path: meta.file.path,
1770
+ baseName: meta.file.baseName
1771
+ }
1772
+ }),
1773
+ footer: resolver.default.footer(ctx.meta, {
1774
+ output,
1775
+ config,
1776
+ file: {
1777
+ path: meta.file.path,
1778
+ baseName: meta.file.baseName
1779
+ }
1780
+ }),
1781
+ children: [
1782
+ paramGroupTypes,
1783
+ responseTypes,
1784
+ requestType,
1785
+ optionsType,
1786
+ responsesType,
1787
+ responseType
1788
+ ]
1789
+ });
1790
+ }
1791
+ });
1792
+ //#endregion
1793
+ //#region src/resolvers/resolverTs.ts
1794
+ /**
1795
+ * Default resolver used by `@kubb/plugin-ts`. Decides the names and file paths
1796
+ * for every generated TypeScript type. Import this in other plugins that need
1797
+ * to reference the exact names `plugin-ts` produces without duplicating the
1798
+ * casing/file-layout rules.
1799
+ *
1800
+ * The `default` helpers are supplied by `createResolver`. This plugin overrides the top-level `name`
1801
+ * to use PascalCase for value and type names and `file` to write PascalCase file paths (dotted names
1802
+ * become `/`-joined), and groups the operation-specific naming under the `param`, `response`, and
1803
+ * `enum` namespaces.
1804
+ *
1805
+ * @example Resolve a type and file name
1806
+ * ```ts
1807
+ * import { resolverTs } from '@kubb/plugin-ts'
1808
+ *
1809
+ * resolverTs.name('list pets') // 'ListPets'
1810
+ * resolverTs.response.status(node, 200) // 'ListPetsStatus200'
1811
+ * ```
1812
+ */
1813
+ const resolverTs = createResolver({
1814
+ pluginName: "plugin-ts",
1815
+ name(name) {
1816
+ return ensureValidVarName(pascalCase(name));
1817
+ },
1818
+ file: createCasedFile(pascalCase),
1819
+ param: createOperationParamResolver(),
1820
+ response: {
1821
+ ...createOperationResponseResolver(),
1822
+ options(node) {
1823
+ return this.name(`${node.operationId} Options`);
1824
+ }
1825
+ },
1826
+ enum: { keyName(node, enumTypeSuffix = "key") {
1827
+ return `${this.name(node.name ?? "")}${enumTypeSuffix}`;
1828
+ } }
1829
+ });
1830
+ //#endregion
1831
+ //#region src/plugin.ts
1832
+ /**
1833
+ * Canonical plugin name for `@kubb/plugin-ts`. Used for driver lookups and
1834
+ * cross-plugin dependency references.
1835
+ */
1836
+ const pluginTsName = "plugin-ts";
1837
+ /**
1838
+ * Generates TypeScript `type` aliases and `interface` declarations from an
1839
+ * OpenAPI spec. The foundation that every other Kubb plugin builds on:
1840
+ * clients, query hooks, mocks, and validators all reference the names this
1841
+ * plugin produces.
1842
+ *
1843
+ * @example
1844
+ * ```ts
1845
+ * import { defineConfig } from 'kubb/config'
1846
+ * import { pluginTs } from '@kubb/plugin-ts'
1847
+ *
1848
+ * export default defineConfig({
1849
+ * input: './petStore.yaml',
1850
+ * output: { path: './src/gen' },
1851
+ * plugins: [
1852
+ * pluginTs({
1853
+ * output: { path: './types' },
1854
+ * enum: { type: 'asConst' },
1855
+ * optionalType: 'questionTokenAndUndefined',
1856
+ * }),
1857
+ * ],
1858
+ * })
1859
+ * ```
1860
+ */
1861
+ const pluginTs = definePlugin((options) => {
1862
+ const { output = {
1863
+ path: "types",
1864
+ barrel: { type: "named" }
1865
+ }, group, exclude = [], include, override = [], enum: enumOptions = {}, optionalType = "questionToken", arrayType = "array", syntaxType = "type", printer, resolver: userResolver, macros: userMacros } = options;
1866
+ const groupConfig = createGroupConfig(group);
1867
+ const resolvedEnum = {
1868
+ type: enumOptions.type ?? "asConst",
1869
+ constCasing: enumOptions.constCasing ?? "camelCase",
1870
+ typeSuffix: enumOptions.typeSuffix ?? "Key",
1871
+ keyCasing: enumOptions.keyCasing ?? "none"
1872
+ };
1873
+ return {
1874
+ name: pluginTsName,
1875
+ options,
1876
+ hooks: { "kubb:plugin:setup"(ctx) {
1877
+ ctx.setOptions({
1878
+ output,
1879
+ exclude,
1880
+ include,
1881
+ override,
1882
+ optionalType,
1883
+ group: groupConfig,
1884
+ arrayType,
1885
+ enum: resolvedEnum,
1886
+ syntaxType,
1887
+ printer
1888
+ });
1889
+ ctx.setResolver(userResolver ? Resolver.merge(resolverTs, userResolver) : resolverTs);
1890
+ if (userMacros?.length) ctx.setMacros(userMacros);
1891
+ ctx.addGenerator(typeGenerator);
1892
+ } }
1893
+ };
1894
+ });
1895
+ //#endregion
1896
+ //#region src/printers/functionParams.ts
1897
+ /**
1898
+ * Narrows a {@link TypeExpression} to a {@link TypeLiteralNode}.
1899
+ */
1900
+ function isTypeLiteral(type) {
1901
+ return typeof type !== "string" && type.kind === "TypeLiteral";
1902
+ }
1903
+ /**
1904
+ * Narrows a {@link TypeExpression} to an {@link IndexedAccessTypeNode}.
1905
+ */
1906
+ function isIndexedAccessType(type) {
1907
+ return typeof type !== "string" && type.kind === "IndexedAccessType";
1908
+ }
1909
+ /**
1910
+ * Creates a {@link TypeLiteralNode} representing an inline anonymous object type.
1911
+ *
1912
+ * @example
1913
+ * ```ts
1914
+ * createTypeLiteral({ members: [{ name: 'petId', type: 'string', optional: false }] })
1915
+ * // { petId: string }
1916
+ * ```
1917
+ */
1918
+ function createTypeLiteral(props) {
1919
+ return {
1920
+ kind: "TypeLiteral",
1921
+ ...props
1922
+ };
1923
+ }
1924
+ /**
1925
+ * Creates an {@link IndexedAccessTypeNode} representing a single field accessed from a named type.
1926
+ *
1927
+ * @example
1928
+ * ```ts
1929
+ * createIndexedAccessType({ target: 'DeletePetPathParams', key: 'petId' })
1930
+ * // DeletePetPathParams['petId']
1931
+ * ```
1932
+ */
1933
+ function createIndexedAccessType(props) {
1934
+ return {
1935
+ kind: "IndexedAccessType",
1936
+ ...props
1937
+ };
1938
+ }
1939
+ /**
1940
+ * Creates an {@link ObjectBindingPatternNode} for a destructured parameter binding.
1941
+ *
1942
+ * @example
1943
+ * ```ts
1944
+ * createObjectBindingPattern({ elements: [{ name: 'id' }, { name: 'name' }] })
1945
+ * // { id, name }
1946
+ * ```
1947
+ */
1948
+ function createObjectBindingPattern(props) {
1949
+ return {
1950
+ kind: "ObjectBindingPattern",
1951
+ ...props
1952
+ };
1953
+ }
1954
+ /**
1955
+ * Creates a {@link FunctionParameterNode}. `optional` defaults to `false`.
1956
+ * Passing `properties` builds a destructured group: an {@link ObjectBindingPatternNode} name
1957
+ * paired with a {@link TypeLiteralNode} type.
1958
+ *
1959
+ * @example Optional param
1960
+ * ```ts
1961
+ * createFunctionParameter({ name: 'params', type: 'QueryParams', optional: true })
1962
+ * // → params?: QueryParams
1963
+ * ```
1964
+ *
1965
+ * @example Destructured group
1966
+ * ```ts
1967
+ * createFunctionParameter({ properties: [{ name: 'id', type: 'string' }, { name: 'name', type: 'string', optional: true }], default: '{}' })
1968
+ * // → { id, name }: { id: string; name?: string } = {}
1969
+ * ```
1970
+ */
1971
+ function createFunctionParameter(input) {
1972
+ if ("properties" in input) return {
1973
+ kind: "FunctionParameter",
1974
+ name: createObjectBindingPattern({ elements: input.properties.map((p) => ({ name: p.name })) }),
1975
+ type: createTypeLiteral({ members: input.properties.map((p) => ({
1976
+ name: p.name,
1977
+ type: p.type,
1978
+ optional: p.optional ?? false
1979
+ })) }),
1980
+ optional: input.optional ?? false,
1981
+ ...input.default !== void 0 ? { default: input.default } : {}
1982
+ };
1983
+ return {
1984
+ kind: "FunctionParameter",
1985
+ optional: false,
1986
+ ...input
1987
+ };
1988
+ }
1989
+ /**
1990
+ * Creates a {@link FunctionParametersNode} from an ordered list of parameters.
1991
+ *
1992
+ * @example
1993
+ * ```ts
1994
+ * const empty = createFunctionParameters()
1995
+ * // { params: [] }
1996
+ * ```
1997
+ */
1998
+ function createFunctionParameters(props = {}) {
1999
+ return {
2000
+ kind: "FunctionParameters",
2001
+ params: [],
2002
+ ...props
2003
+ };
2004
+ }
2005
+ //#endregion
2006
+ //#region src/printers/functionPrinter.ts
2007
+ /**
2008
+ * Renders a {@link TypeExpression} to its TypeScript source.
2009
+ *
2010
+ * - a `string` is a type reference, returned as-is
2011
+ * - an `IndexedAccessType` becomes `objectType['indexType']`
2012
+ * - a `TypeLiteral` becomes `{ key: Type; key?: Type }`
2013
+ *
2014
+ * `transformType` is applied once to the fully rendered type, matching how the
2015
+ * printer wrapped reference types before the `ts.factory` model.
2016
+ */
2017
+ function renderType(type, transformType) {
2018
+ const rendered = renderTypeExpression(type);
2019
+ return transformType ? transformType(rendered) : rendered;
2020
+ }
2021
+ /**
2022
+ * Renders an object-type key, quoting it as a string literal when it is not a valid
2023
+ * identifier so `{ 'content-type': string }` stays valid TypeScript.
2024
+ */
2025
+ function renderKey(name) {
2026
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
2027
+ }
2028
+ function renderTypeExpression(type) {
2029
+ if (typeof type === "string") return type;
2030
+ if (isIndexedAccessType(type)) return `${type.target}['${type.key}']`;
2031
+ return `{ ${type.members.map((member) => {
2032
+ const value = renderTypeExpression(member.type);
2033
+ return member.optional ? `${renderKey(member.name)}?: ${value}` : `${renderKey(member.name)}: ${value}`;
2034
+ }).join("; ")} }`;
2035
+ }
2036
+ function groupMembers(param) {
2037
+ if (typeof param.name === "string") return null;
2038
+ return param.type && isTypeLiteral(param.type) ? param.type.members : [];
2039
+ }
2040
+ function rank(param) {
2041
+ if (param.rest) return PARAM_RANK.rest;
2042
+ if (param.default) return PARAM_RANK.withDefault;
2043
+ const members = groupMembers(param);
2044
+ if (members) return members.every((m) => m.optional) ? PARAM_RANK.optional : PARAM_RANK.required;
2045
+ return param.optional ? PARAM_RANK.optional : PARAM_RANK.required;
2046
+ }
2047
+ function sortParams(params) {
2048
+ return params.toSorted((a, b) => rank(a) - rank(b));
2049
+ }
2050
+ function sortedGroupMembers(name, type) {
2051
+ const members = type && isTypeLiteral(type) ? type.members : [];
2052
+ const memberRank = (optional) => optional ? PARAM_RANK.optional : PARAM_RANK.required;
2053
+ return name.elements.map((element, index) => ({
2054
+ name: element.name,
2055
+ propertyName: element.propertyName,
2056
+ type: members[index]?.type,
2057
+ optional: members[index]?.optional
2058
+ })).toSorted((a, b) => memberRank(a.optional) - memberRank(b.optional));
2059
+ }
2060
+ /**
2061
+ * Renders one binding element: the local name, prefixed with its source key when the
2062
+ * binding renames the property, as in `{ petId: id }`.
2063
+ */
2064
+ function renderBindingMember(member, transformName) {
2065
+ const local = transformName ? transformName(member.name) : member.name;
2066
+ return member.propertyName ? `${member.propertyName}: ${local}` : local;
2067
+ }
2068
+ /**
2069
+ * Renders the type annotation of a destructured group. An inline object type is
2070
+ * rendered from the already-sorted members so its key order matches the binding;
2071
+ * a reference type is rendered as-is.
2072
+ */
2073
+ function renderGroupType(type, sorted, transformType) {
2074
+ if (!type) return void 0;
2075
+ if (!isTypeLiteral(type)) return renderType(type);
2076
+ const typed = sorted.filter((member) => member.type !== void 0);
2077
+ if (!typed.length) return void 0;
2078
+ return `{ ${typed.map((member) => {
2079
+ const key = renderKey(member.propertyName ?? member.name);
2080
+ const value = renderType(member.type, transformType);
2081
+ return member.optional ? `${key}?: ${value}` : `${key}: ${value}`;
2082
+ }).join("; ")} }`;
2083
+ }
2084
+ function printParameter(node, options) {
2085
+ const { mode, transformName, transformType } = options;
2086
+ const bindingName = node.name;
2087
+ if (mode === "call") {
2088
+ if (typeof bindingName !== "string") return `{ ${sortedGroupMembers(bindingName, node.type).map((member) => renderBindingMember(member)).join(", ")} }`;
2089
+ const name = transformName ? transformName(bindingName) : bindingName;
2090
+ return node.rest ? `...${name}` : name;
2091
+ }
2092
+ if (typeof bindingName !== "string") {
2093
+ const sorted = sortedGroupMembers(bindingName, node.type);
2094
+ const binding = `{ ${sorted.map((member) => renderBindingMember(member, transformName)).join(", ")} }`;
2095
+ const allOptional = sorted.every((member) => member.optional);
2096
+ const type = renderGroupType(node.type, sorted, transformType);
2097
+ if (type) {
2098
+ if (allOptional) return `${binding}: ${type} = ${node.default ?? "{}"}`;
2099
+ return node.default ? `${binding}: ${type} = ${node.default}` : `${binding}: ${type}`;
2100
+ }
2101
+ return node.default ? `${binding} = ${node.default}` : binding;
2102
+ }
2103
+ const name = transformName ? transformName(bindingName) : bindingName;
2104
+ const type = node.type ? renderType(node.type, transformType) : void 0;
2105
+ if (node.rest) return type ? `...${name}: ${type}` : `...${name}`;
2106
+ if (type) {
2107
+ if (node.optional) return `${name}?: ${type}`;
2108
+ return node.default ? `${name}: ${type} = ${node.default}` : `${name}: ${type}`;
2109
+ }
2110
+ return node.default ? `${name} = ${node.default}` : name;
2111
+ }
2112
+ /**
2113
+ * Default function-signature printer. Renders a parameter list in one of two modes:
2114
+ * `declaration` for the function signature and `call` for the call arguments.
2115
+ *
2116
+ * @example
2117
+ * ```ts
2118
+ * const printer = functionPrinter({ mode: 'declaration' })
2119
+ *
2120
+ * const sig = createFunctionParameters({
2121
+ * params: [
2122
+ * createFunctionParameter({ name: 'petId', type: 'string', optional: false }),
2123
+ * createFunctionParameter({ name: 'config', type: 'Config', optional: false, default: '{}' }),
2124
+ * ],
2125
+ * })
2126
+ *
2127
+ * printer.print(sig) // → "petId: string, config: Config = {}"
2128
+ * ```
2129
+ */
2130
+ function functionPrinter(options) {
2131
+ return {
2132
+ name: "functionParameters",
2133
+ options,
2134
+ print(node) {
2135
+ return sortParams(node.params).map((p) => printParameter(p, options)).filter(Boolean).join(", ");
2136
+ }
2137
+ };
2138
+ }
2139
+ //#endregion
2140
+ //#region src/printers/operationParams.ts
2141
+ /**
2142
+ * Resolves the {@link TypeExpression} for an individual parameter.
2143
+ *
2144
+ * Without a resolver, it falls back to the schema primitive (a plain type-name string). When the
2145
+ * parameter belongs to a named group, it emits an `IndexedAccessType` like `GroupParams['petId']`,
2146
+ * otherwise the resolved individual name.
2147
+ */
2148
+ function resolveParamType({ node, param, resolver }) {
2149
+ if (!resolver) return param.schema.primitive ?? "unknown";
2150
+ const individualName = resolver.param.name(node, param);
2151
+ const groupLocation = param.in === "path" || param.in === "query" || param.in === "header" ? param.in : void 0;
2152
+ const groupResolvers = {
2153
+ path: resolver.param.path,
2154
+ query: resolver.param.query,
2155
+ header: resolver.param.headers
2156
+ };
2157
+ const groupName = groupLocation ? groupResolvers[groupLocation].call(resolver, node, param) : void 0;
2158
+ if (groupName && groupName !== individualName) return createIndexedAccessType({
2159
+ target: groupName,
2160
+ key: param.name
2161
+ });
2162
+ return individualName;
2163
+ }
2164
+ /**
2165
+ * Derives a {@link ParamGroupType} for a query or header group from the resolver.
2166
+ *
2167
+ * Returns `null` when there is no resolver, no params, or the group name equals the
2168
+ * individual param name (so there is no real group to emit).
2169
+ */
2170
+ function resolveGroupType({ node, params, group, resolver }) {
2171
+ if (!resolver || !params.length) return null;
2172
+ const firstParam = params[0];
2173
+ const groupName = (group === "query" ? resolver.param.query : resolver.param.headers).call(resolver, node, firstParam);
2174
+ if (groupName === resolver.param.name(node, firstParam)) return null;
2175
+ return {
2176
+ type: groupName,
2177
+ optional: params.every((p) => !p.required)
2178
+ };
2179
+ }
2180
+ /**
2181
+ * Converts an `OperationNode` into function parameters for code generation.
2182
+ *
2183
+ * Centralizes parameter grouping logic for all plugins. `paramsType` chooses between one
2184
+ * destructured object parameter (`object`) and separate top-level parameters (`inline`), while
2185
+ * `pathParamsType` controls how path params render in inline mode. Provide a `resolver` for type
2186
+ * name resolution and `extraParams` for plugin-specific trailing parameters such as an `options` object.
2187
+ */
2188
+ function createOperationParams(node, options) {
2189
+ const { paramsType, pathParamsType, resolver, pathParamsDefault, extraParams = [], paramNames, typeWrapper } = options;
2190
+ const dataName = paramNames?.data ?? "data";
2191
+ const paramsName = paramNames?.params ?? "params";
2192
+ const headersName = paramNames?.headers ?? "headers";
2193
+ const pathName = paramNames?.path ?? "pathParams";
2194
+ const wrapType = (type) => typeWrapper ? typeWrapper(type) : type;
2195
+ const wrapTypeExpression = (type) => typeof type === "string" ? wrapType(type) : type;
2196
+ const { path: pathParams, query: queryParams, header: headerParams } = getOperationParameters(node);
2197
+ const toProperty = (param) => ({
2198
+ name: param.name,
2199
+ type: wrapTypeExpression(resolveParamType({
2200
+ node,
2201
+ param,
2202
+ resolver
2203
+ })),
2204
+ optional: !param.required
2205
+ });
2206
+ const emptyObjectDefault = (props) => props.every((p) => p.optional) ? "{}" : void 0;
2207
+ const bodyType = node.requestBody?.content?.[0]?.schema ? wrapType(resolver?.response.body(node) ?? "unknown") : void 0;
2208
+ const bodyProperty = bodyType ? [{
2209
+ name: dataName,
2210
+ type: bodyType,
2211
+ optional: !(node.requestBody?.required ?? false)
2212
+ }] : [];
2213
+ const trailingGroups = [{
2214
+ name: paramsName,
2215
+ node,
2216
+ params: queryParams,
2217
+ groupType: resolveGroupType({
2218
+ node,
2219
+ params: queryParams,
2220
+ group: "query",
2221
+ resolver
2222
+ }),
2223
+ resolver,
2224
+ wrapType
2225
+ }, {
2226
+ name: headersName,
2227
+ node,
2228
+ params: headerParams,
2229
+ groupType: resolveGroupType({
2230
+ node,
2231
+ params: headerParams,
2232
+ group: "header",
2233
+ resolver
2234
+ }),
2235
+ resolver,
2236
+ wrapType
2237
+ }];
2238
+ const params = [];
2239
+ if (paramsType === "object") {
2240
+ const children = [
2241
+ ...pathParams.map(toProperty),
2242
+ ...bodyProperty,
2243
+ ...trailingGroups.flatMap(buildGroupProperty)
2244
+ ];
2245
+ if (children.length) params.push(createFunctionParameter({
2246
+ properties: children,
2247
+ default: emptyObjectDefault(children)
2248
+ }));
2249
+ } else {
2250
+ if (pathParamsType === "inlineSpread" && pathParams.length) {
2251
+ const spreadType = resolver?.param.path(node, pathParams[0]);
2252
+ params.push(createFunctionParameter({
2253
+ name: pathName,
2254
+ type: spreadType ? wrapType(spreadType) : void 0,
2255
+ rest: true
2256
+ }));
2257
+ } else if (pathParamsType === "inline") params.push(...pathParams.map((p) => createFunctionParameter(toProperty(p))));
2258
+ else if (pathParams.length) {
2259
+ const pathChildren = pathParams.map(toProperty);
2260
+ params.push(createFunctionParameter({
2261
+ properties: pathChildren,
2262
+ default: pathParamsDefault ?? emptyObjectDefault(pathChildren)
2263
+ }));
2264
+ }
2265
+ params.push(...bodyProperty.map((p) => createFunctionParameter(p)));
2266
+ params.push(...trailingGroups.flatMap(buildGroupParam));
2267
+ }
2268
+ params.push(...extraParams);
2269
+ return createFunctionParameters({ params });
2270
+ }
2271
+ /**
2272
+ * Builds the property descriptor for a query or header group.
2273
+ * Returns an empty array when there are no params to emit.
2274
+ *
2275
+ * A pre-resolved `groupType` emits `name: GroupType`. Otherwise it builds an inline
2276
+ * {@link TypeLiteralNode} from the individual params.
2277
+ */
2278
+ function buildGroupProperty({ name, node, params, groupType, resolver, wrapType }) {
2279
+ if (groupType) return [{
2280
+ name,
2281
+ type: typeof groupType.type === "string" ? wrapType(groupType.type) : groupType.type,
2282
+ optional: groupType.optional
2283
+ }];
2284
+ if (params.length) return [{
2285
+ name,
2286
+ type: buildTypeLiteral({
2287
+ node,
2288
+ params,
2289
+ resolver
2290
+ }),
2291
+ optional: params.every((p) => !p.required)
2292
+ }];
2293
+ return [];
2294
+ }
2295
+ /**
2296
+ * Builds a single {@link FunctionParameterNode} for a query or header group.
2297
+ * Returns an empty array when there are no params to emit.
2298
+ */
2299
+ function buildGroupParam(args) {
2300
+ return buildGroupProperty(args).map((p) => createFunctionParameter(p));
2301
+ }
2302
+ /**
2303
+ * Builds a {@link TypeLiteralNode} for an inline anonymous type grouping named fields.
2304
+ *
2305
+ * Used when query or header parameters have no dedicated group type name.
2306
+ */
2307
+ function buildTypeLiteral({ node, params, resolver }) {
2308
+ return createTypeLiteral({ members: params.map((p) => ({
2309
+ name: p.name,
2310
+ type: resolveParamType({
2311
+ node,
2312
+ param: p,
2313
+ resolver
2314
+ }),
2315
+ optional: !p.required
2316
+ })) });
2317
+ }
2318
+ //#endregion
2319
+ export { Enum, Type, buildParams, createFunctionParameter, createFunctionParameters, createIndexedAccessType, createObjectBindingPattern, createOperationParams, createTypeLiteral, pluginTs as default, pluginTs, functionPrinter, pluginTsName, printerTs, renderType, resolverTs, typeGenerator };
2320
+
2321
+ //# sourceMappingURL=index.js.map