zod-codegen 1.6.3 → 1.7.1

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 (120) hide show
  1. package/.github/workflows/ci.yml +50 -48
  2. package/.github/workflows/release.yml +13 -3
  3. package/.husky/commit-msg +1 -1
  4. package/.husky/pre-commit +1 -1
  5. package/.lintstagedrc.json +5 -1
  6. package/.nvmrc +1 -1
  7. package/.prettierrc.json +12 -5
  8. package/CHANGELOG.md +17 -0
  9. package/CONTRIBUTING.md +12 -12
  10. package/EXAMPLES.md +135 -57
  11. package/PERFORMANCE.md +4 -4
  12. package/README.md +87 -64
  13. package/SECURITY.md +1 -1
  14. package/dist/src/cli.js +11 -18
  15. package/dist/src/generator.d.ts +2 -2
  16. package/dist/src/generator.d.ts.map +1 -1
  17. package/dist/src/generator.js +5 -3
  18. package/dist/src/interfaces/code-generator.d.ts.map +1 -1
  19. package/dist/src/services/code-generator.service.d.ts +3 -1
  20. package/dist/src/services/code-generator.service.d.ts.map +1 -1
  21. package/dist/src/services/code-generator.service.js +236 -219
  22. package/dist/src/services/file-reader.service.d.ts +2 -0
  23. package/dist/src/services/file-reader.service.d.ts.map +1 -1
  24. package/dist/src/services/file-reader.service.js +25 -11
  25. package/dist/src/services/file-writer.service.d.ts.map +1 -1
  26. package/dist/src/services/file-writer.service.js +2 -2
  27. package/dist/src/services/import-builder.service.d.ts.map +1 -1
  28. package/dist/src/services/import-builder.service.js +3 -3
  29. package/dist/src/services/type-builder.service.d.ts.map +1 -1
  30. package/dist/src/types/generator-options.d.ts.map +1 -1
  31. package/dist/src/types/openapi.d.ts.map +1 -1
  32. package/dist/src/types/openapi.js +20 -20
  33. package/dist/src/utils/error-handler.d.ts.map +1 -1
  34. package/dist/src/utils/naming-convention.d.ts.map +1 -1
  35. package/dist/src/utils/naming-convention.js +6 -3
  36. package/dist/src/utils/signal-handler.d.ts.map +1 -1
  37. package/dist/tests/integration/cli-comprehensive.test.d.ts +2 -0
  38. package/dist/tests/integration/cli-comprehensive.test.d.ts.map +1 -0
  39. package/dist/tests/integration/cli-comprehensive.test.js +123 -0
  40. package/dist/tests/integration/cli.test.d.ts +2 -0
  41. package/dist/tests/integration/cli.test.d.ts.map +1 -0
  42. package/dist/tests/integration/cli.test.js +25 -0
  43. package/dist/tests/integration/error-scenarios.test.d.ts +2 -0
  44. package/dist/tests/integration/error-scenarios.test.d.ts.map +1 -0
  45. package/dist/tests/integration/error-scenarios.test.js +169 -0
  46. package/dist/tests/integration/snapshots.test.d.ts +2 -0
  47. package/dist/tests/integration/snapshots.test.d.ts.map +1 -0
  48. package/dist/tests/integration/snapshots.test.js +100 -0
  49. package/dist/tests/unit/code-generator-edge-cases.test.d.ts +2 -0
  50. package/dist/tests/unit/code-generator-edge-cases.test.d.ts.map +1 -0
  51. package/dist/tests/unit/code-generator-edge-cases.test.js +506 -0
  52. package/dist/tests/unit/code-generator.test.d.ts +2 -0
  53. package/dist/tests/unit/code-generator.test.d.ts.map +1 -0
  54. package/dist/tests/unit/code-generator.test.js +1364 -0
  55. package/dist/tests/unit/file-reader.test.d.ts +2 -0
  56. package/dist/tests/unit/file-reader.test.d.ts.map +1 -0
  57. package/dist/tests/unit/file-reader.test.js +153 -0
  58. package/dist/tests/unit/generator.test.d.ts +2 -0
  59. package/dist/tests/unit/generator.test.d.ts.map +1 -0
  60. package/dist/tests/unit/generator.test.js +119 -0
  61. package/dist/tests/unit/naming-convention.test.d.ts +2 -0
  62. package/dist/tests/unit/naming-convention.test.d.ts.map +1 -0
  63. package/dist/tests/unit/naming-convention.test.js +256 -0
  64. package/dist/tests/unit/reporter.test.d.ts +2 -0
  65. package/dist/tests/unit/reporter.test.d.ts.map +1 -0
  66. package/dist/tests/unit/reporter.test.js +44 -0
  67. package/dist/tests/unit/type-builder.test.d.ts +2 -0
  68. package/dist/tests/unit/type-builder.test.d.ts.map +1 -0
  69. package/dist/tests/unit/type-builder.test.js +108 -0
  70. package/dist/vitest.config.d.ts.map +1 -1
  71. package/dist/vitest.config.js +10 -20
  72. package/eslint.config.mjs +38 -28
  73. package/examples/.gitkeep +1 -1
  74. package/examples/README.md +4 -2
  75. package/examples/petstore/README.md +18 -17
  76. package/examples/petstore/{type.ts → api.ts} +158 -74
  77. package/examples/petstore/authenticated-usage.ts +6 -4
  78. package/examples/petstore/basic-usage.ts +4 -3
  79. package/examples/petstore/error-handling-usage.ts +84 -0
  80. package/examples/petstore/retry-handler-usage.ts +11 -18
  81. package/examples/petstore/server-variables-usage.ts +10 -10
  82. package/examples/pokeapi/README.md +8 -8
  83. package/examples/pokeapi/api.ts +218 -0
  84. package/examples/pokeapi/basic-usage.ts +3 -2
  85. package/examples/pokeapi/custom-client.ts +5 -4
  86. package/package.json +17 -21
  87. package/src/cli.ts +20 -25
  88. package/src/generator.ts +13 -11
  89. package/src/interfaces/code-generator.ts +1 -1
  90. package/src/services/code-generator.service.ts +799 -1120
  91. package/src/services/file-reader.service.ts +35 -15
  92. package/src/services/file-writer.service.ts +7 -7
  93. package/src/services/import-builder.service.ts +9 -13
  94. package/src/services/type-builder.service.ts +8 -19
  95. package/src/types/generator-options.ts +1 -1
  96. package/src/types/openapi.ts +22 -22
  97. package/src/utils/error-handler.ts +2 -2
  98. package/src/utils/naming-convention.ts +13 -10
  99. package/src/utils/reporter.ts +2 -2
  100. package/src/utils/signal-handler.ts +7 -8
  101. package/tests/integration/cli-comprehensive.test.ts +53 -31
  102. package/tests/integration/cli.test.ts +5 -5
  103. package/tests/integration/error-scenarios.test.ts +20 -26
  104. package/tests/integration/snapshots.test.ts +19 -23
  105. package/tests/unit/code-generator-edge-cases.test.ts +133 -133
  106. package/tests/unit/code-generator.test.ts +431 -330
  107. package/tests/unit/file-reader.test.ts +58 -18
  108. package/tests/unit/generator.test.ts +30 -18
  109. package/tests/unit/naming-convention.test.ts +27 -27
  110. package/tests/unit/type-builder.test.ts +2 -2
  111. package/tsconfig.json +5 -3
  112. package/vitest.config.ts +11 -21
  113. package/dist/scripts/update-manifest.d.ts +0 -14
  114. package/dist/scripts/update-manifest.d.ts.map +0 -1
  115. package/dist/scripts/update-manifest.js +0 -33
  116. package/dist/src/assets/manifest.json +0 -5
  117. package/examples/pokeapi/type.ts +0 -109
  118. package/generated/type.ts +0 -371
  119. package/scripts/update-manifest.ts +0 -49
  120. package/src/assets/manifest.json +0 -5
@@ -1,10 +1,9 @@
1
- import jp from 'jsonpath';
2
1
  import * as ts from 'typescript';
3
2
  import { z } from 'zod';
4
3
  import { MethodSchema, Reference, SchemaProperties } from '../types/openapi.js';
4
+ import { transformNamingConvention } from '../utils/naming-convention.js';
5
5
  import { TypeScriptImportBuilderService } from './import-builder.service.js';
6
6
  import { TypeScriptTypeBuilderService } from './type-builder.service.js';
7
- import { transformNamingConvention, } from '../utils/naming-convention.js';
8
7
  export class TypeScriptCodeGeneratorService {
9
8
  typeBuilder = new TypeScriptTypeBuilderService();
10
9
  importBuilder = new TypeScriptImportBuilderService();
@@ -20,10 +19,33 @@ export class TypeScriptCodeGeneratorService {
20
19
  }
21
20
  ZodAST = z.object({
22
21
  type: z.enum(['string', 'number', 'boolean', 'object', 'array', 'unknown', 'record']),
23
- args: z.array(z.unknown()).optional(),
22
+ args: z.array(z.unknown()).optional()
24
23
  });
24
+ extractSchemaReferences(schema) {
25
+ const references = new Set();
26
+ const walk = (node) => {
27
+ if (Array.isArray(node)) {
28
+ for (const item of node) {
29
+ walk(item);
30
+ }
31
+ return;
32
+ }
33
+ if (node === null || typeof node !== 'object') {
34
+ return;
35
+ }
36
+ for (const [key, value] of Object.entries(node)) {
37
+ if (key === '$ref' && typeof value === 'string' && value.startsWith('#/components/schemas/')) {
38
+ references.add(value.replace('#/components/schemas/', ''));
39
+ continue;
40
+ }
41
+ walk(value);
42
+ }
43
+ };
44
+ walk(schema);
45
+ return [...references];
46
+ }
25
47
  generate(spec) {
26
- const file = ts.createSourceFile('generated.ts', '', ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
48
+ const file = ts.createSourceFile('api.ts', '', ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
27
49
  const nodes = this.buildAST(spec);
28
50
  return this.printer.printList(ts.ListFormat.MultiLine, ts.factory.createNodeArray(nodes), file);
29
51
  }
@@ -63,8 +85,9 @@ export class TypeScriptCodeGeneratorService {
63
85
  ...Object.values(schemas),
64
86
  ...schemaTypeAliases,
65
87
  ...serverConfig,
88
+ this.buildResponseValidationErrorClass(),
66
89
  this.createComment('Client class'),
67
- clientClass,
90
+ clientClass
68
91
  ];
69
92
  }
70
93
  buildSchemas(openapi) {
@@ -75,8 +98,9 @@ export class TypeScriptCodeGeneratorService {
75
98
  const sortedSchemaNames = this.topologicalSort(schemasMap);
76
99
  return sortedSchemaNames.reduce((schemaRegistered, name) => {
77
100
  const schema = openapi.components?.schemas?.[name];
78
- if (!schema)
101
+ if (!schema) {
79
102
  return schemaRegistered;
103
+ }
80
104
  // Set context for current schema being built
81
105
  this.currentSchemaName = name;
82
106
  const schemaExpression = this.buildSchema(schema);
@@ -84,13 +108,13 @@ export class TypeScriptCodeGeneratorService {
84
108
  this.currentSchemaName = null;
85
109
  const sanitizedName = this.typeBuilder.sanitizeIdentifier(name);
86
110
  // Add type annotation: z.ZodType<Name>
87
- const typeAnnotation = ts.factory.createTypeReferenceNode(ts.factory.createQualifiedName(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('ZodType')), [ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedName), undefined)]);
88
- const variableStatement = ts.factory.createVariableStatement([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([
89
- ts.factory.createVariableDeclaration(ts.factory.createIdentifier(sanitizedName), undefined, typeAnnotation, schemaExpression),
90
- ], ts.NodeFlags.Const));
111
+ const typeAnnotation = ts.factory.createTypeReferenceNode(ts.factory.createQualifiedName(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('ZodType')), [
112
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedName), undefined)
113
+ ]);
114
+ const variableStatement = ts.factory.createVariableStatement([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(ts.factory.createIdentifier(sanitizedName), undefined, typeAnnotation, schemaExpression)], ts.NodeFlags.Const));
91
115
  return {
92
116
  ...schemaRegistered,
93
- [name]: variableStatement,
117
+ [name]: variableStatement
94
118
  };
95
119
  }, {});
96
120
  }
@@ -109,8 +133,9 @@ export class TypeScriptCodeGeneratorService {
109
133
  const statements = [];
110
134
  for (const name of sortedSchemaNames) {
111
135
  const schema = openapi.components?.schemas?.[name];
112
- if (!schema)
136
+ if (!schema) {
113
137
  continue;
138
+ }
114
139
  const sanitizedName = this.typeBuilder.sanitizeIdentifier(name);
115
140
  const safeSchema = SchemaProperties.safeParse(schema);
116
141
  if (!safeSchema.success) {
@@ -201,9 +226,7 @@ export class TypeScriptCodeGeneratorService {
201
226
  case 'boolean':
202
227
  return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
203
228
  case 'array': {
204
- const itemsType = prop['items']
205
- ? this.buildTypeNode(prop['items'])
206
- : ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
229
+ const itemsType = prop['items'] ? this.buildTypeNode(prop['items']) : ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
207
230
  return ts.factory.createArrayTypeNode(itemsType);
208
231
  }
209
232
  case 'object': {
@@ -215,7 +238,7 @@ export class TypeScriptCodeGeneratorService {
215
238
  // Empty object or additionalProperties - use Record<string, unknown>
216
239
  return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Record'), [
217
240
  ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
218
- ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
241
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)
219
242
  ]);
220
243
  }
221
244
  default:
@@ -246,6 +269,25 @@ export class TypeScriptCodeGeneratorService {
246
269
  });
247
270
  return ts.factory.createInterfaceDeclaration([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier(name), undefined, undefined, members);
248
271
  }
272
+ buildResponseValidationErrorClass() {
273
+ const typeParamT = ts.factory.createTypeParameterDeclaration(undefined, 'T');
274
+ const responseProperty = ts.factory.createPropertyDeclaration([ts.factory.createToken(ts.SyntaxKind.ReadonlyKeyword)], 'response', undefined, ts.factory.createTypeReferenceNode('Response'), undefined);
275
+ const errorProperty = ts.factory.createPropertyDeclaration([ts.factory.createToken(ts.SyntaxKind.ReadonlyKeyword)], 'error', undefined, ts.factory.createTypeReferenceNode(ts.factory.createQualifiedName(ts.factory.createIdentifier('z'), 'ZodError'), [ts.factory.createTypeReferenceNode('T')]), undefined);
276
+ const constructorDecl = ts.factory.createConstructorDeclaration(undefined, [
277
+ ts.factory.createParameterDeclaration(undefined, undefined, 'message', undefined, ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)),
278
+ ts.factory.createParameterDeclaration(undefined, undefined, 'response', undefined, ts.factory.createTypeReferenceNode('Response')),
279
+ ts.factory.createParameterDeclaration(undefined, undefined, 'error', undefined, ts.factory.createTypeReferenceNode(ts.factory.createQualifiedName(ts.factory.createIdentifier('z'), 'ZodError'), [ts.factory.createTypeReferenceNode('T')]))
280
+ ], ts.factory.createBlock([
281
+ ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createSuper(), undefined, [ts.factory.createIdentifier('message')])),
282
+ ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 'name'), ts.SyntaxKind.EqualsToken, ts.factory.createAsExpression(ts.factory.createStringLiteral('ResponseValidationError', true), ts.factory.createTypeReferenceNode('const')))),
283
+ ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 'response'), ts.SyntaxKind.EqualsToken, ts.factory.createIdentifier('response'))),
284
+ ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 'error'), ts.SyntaxKind.EqualsToken, ts.factory.createIdentifier('error')))
285
+ ], true));
286
+ const dataGetter = ts.factory.createGetAccessorDeclaration(undefined, 'data', [], ts.factory.createTypeReferenceNode('T'), ts.factory.createBlock([
287
+ ts.factory.createReturnStatement(ts.factory.createAsExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), 'response'), 'json'), undefined, []), ts.factory.createTypeReferenceNode('T')))
288
+ ], true));
289
+ return ts.factory.createClassDeclaration([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], 'ResponseValidationError', [typeParamT], [ts.factory.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [ts.factory.createExpressionWithTypeArguments(ts.factory.createIdentifier('Error'), undefined)])], [responseProperty, errorProperty, constructorDecl, dataGetter]);
290
+ }
249
291
  buildClientClass(openapi, schemas) {
250
292
  const clientName = this.generateClientName(openapi.info.title);
251
293
  const methods = this.buildClientMethods(openapi, schemas);
@@ -255,7 +297,7 @@ export class TypeScriptCodeGeneratorService {
255
297
  this.buildGetBaseRequestOptionsMethod(),
256
298
  this.buildHandleResponseMethod(),
257
299
  this.buildHttpRequestMethod(),
258
- ...methods,
300
+ ...methods
259
301
  ]);
260
302
  }
261
303
  buildConstructor(openapi) {
@@ -263,24 +305,21 @@ export class TypeScriptCodeGeneratorService {
263
305
  if (hasServers) {
264
306
  // Options-based constructor
265
307
  return ts.factory.createConstructorDeclaration(undefined, [
266
- ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('options'), undefined, ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('ClientOptions'), undefined), undefined),
308
+ ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('options'), undefined, ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('ClientOptions'), undefined), undefined)
267
309
  ], ts.factory.createBlock([
268
310
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
269
311
  ts.factory.createVariableDeclaration(ts.factory.createIdentifier('resolvedUrl'), undefined, undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('baseUrl')), ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), ts.factory.createNull()), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('baseUrl')), ts.factory.createToken(ts.SyntaxKind.ColonToken), ts.factory.createCallExpression(ts.factory.createIdentifier('resolveServerUrl'), undefined, [
270
312
  ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('serverIndex')),
271
- ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('serverVariables')),
272
- ]))),
313
+ ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('serverVariables'))
314
+ ])))
273
315
  ], ts.NodeFlags.Const)),
274
- ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#baseUrl')), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createIdentifier('resolvedUrl'))),
316
+ ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#baseUrl')), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createIdentifier('resolvedUrl')))
275
317
  ], true));
276
318
  }
277
319
  else {
278
320
  // Fallback: simple baseUrl parameter
279
- return ts.factory.createConstructorDeclaration(undefined, [
280
- this.typeBuilder.createParameter('baseUrl', 'string', ts.factory.createStringLiteral('/', true)),
281
- this.typeBuilder.createParameter('_', 'unknown', undefined, true),
282
- ], ts.factory.createBlock([
283
- ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#baseUrl')), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createIdentifier('baseUrl'))),
321
+ return ts.factory.createConstructorDeclaration(undefined, [this.typeBuilder.createParameter('baseUrl', 'string', ts.factory.createStringLiteral('/', true)), this.typeBuilder.createParameter('_', 'unknown', undefined, true)], ts.factory.createBlock([
322
+ ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#baseUrl')), ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createIdentifier('baseUrl')))
284
323
  ], true));
285
324
  }
286
325
  }
@@ -290,9 +329,9 @@ export class TypeScriptCodeGeneratorService {
290
329
  ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('RequestInit'), undefined),
291
330
  ts.factory.createUnionTypeNode([
292
331
  ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral('method', true)),
293
- ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral('body', true)),
294
- ]),
295
- ]),
332
+ ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral('body', true))
333
+ ])
334
+ ])
296
335
  ]), ts.factory.createBlock([ts.factory.createReturnStatement(ts.factory.createObjectLiteralExpression([], false))], true));
297
336
  }
298
337
  buildHandleResponseMethod() {
@@ -300,76 +339,66 @@ export class TypeScriptCodeGeneratorService {
300
339
  this.typeBuilder.createParameter('response', 'Response'),
301
340
  this.typeBuilder.createParameter('method', 'string'),
302
341
  this.typeBuilder.createParameter('path', 'string'),
303
- this.typeBuilder.createParameter('options', '{params?: Record<string, string | number | boolean>; data?: unknown; contentType?: string; headers?: Record<string, string>}'),
304
- ], ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
305
- ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Response'), undefined),
306
- ]), ts.factory.createBlock([ts.factory.createReturnStatement(ts.factory.createIdentifier('response'))], true));
342
+ this.typeBuilder.createParameter('options', '{params?: Record<string, string | number | boolean>; data?: unknown; contentType?: string; headers?: Record<string, string>}')
343
+ ], ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Response'), undefined)]), ts.factory.createBlock([ts.factory.createReturnStatement(ts.factory.createIdentifier('response'))], true));
307
344
  }
308
345
  buildHttpRequestMethod() {
309
346
  return ts.factory.createMethodDeclaration([ts.factory.createToken(ts.SyntaxKind.ProtectedKeyword), ts.factory.createToken(ts.SyntaxKind.AsyncKeyword)], undefined, ts.factory.createIdentifier('makeRequest'), undefined, [this.typeBuilder.createGenericType('T')], [
310
347
  this.typeBuilder.createParameter('method', 'string'),
311
348
  this.typeBuilder.createParameter('path', 'string'),
312
- this.typeBuilder.createParameter('options', '{params?: Record<string, string | number | boolean>; data?: unknown; contentType?: string; headers?: Record<string, string>}', ts.factory.createObjectLiteralExpression([], false), false),
313
- ], ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
314
- ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('T'), undefined),
315
- ]), ts.factory.createBlock([
349
+ this.typeBuilder.createParameter('options', '{params?: Record<string, string | number | boolean>; data?: unknown; contentType?: string; headers?: Record<string, string>}', ts.factory.createObjectLiteralExpression([], false), false)
350
+ ], ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('T'), undefined)]), ts.factory.createBlock([
316
351
  // Create initial URL object that we will use to build the final URL
317
352
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
318
353
  ts.factory.createVariableDeclaration(ts.factory.createIdentifier('baseUrl'), undefined, undefined, ts.factory.createNewExpression(ts.factory.createIdentifier('URL'), undefined, [
319
354
  ts.factory.createIdentifier('path'),
320
- ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#baseUrl')),
321
- ])),
355
+ ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#baseUrl'))
356
+ ]))
322
357
  ], ts.NodeFlags.Const)),
323
358
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
324
- ts.factory.createVariableDeclaration(ts.factory.createIdentifier('url'), undefined, undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params')), ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('keys')), undefined, [
325
- ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params')),
326
- ]), ts.factory.createIdentifier('length')), ts.factory.createToken(ts.SyntaxKind.GreaterThanToken), ts.factory.createNumericLiteral('0'))), undefined, ts.factory.createCallExpression(ts.factory.createParenthesizedExpression(ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
327
- ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('entries')), undefined, [
328
- ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params')),
329
- ]), ts.factory.createIdentifier('filter')), undefined, [
359
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier('url'), undefined, undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params')), ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('keys')), undefined, [ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params'))]), ts.factory.createIdentifier('length')), ts.factory.createToken(ts.SyntaxKind.GreaterThanToken), ts.factory.createNumericLiteral('0'))), undefined, ts.factory.createCallExpression(ts.factory.createParenthesizedExpression(ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
360
+ ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('entries')), undefined, [ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params'))]), ts.factory.createIdentifier('filter')), undefined, [
330
361
  ts.factory.createArrowFunction(undefined, undefined, [
331
362
  ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createArrayBindingPattern([
332
363
  ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier(''), undefined),
333
- ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('value'), undefined),
334
- ]), undefined, undefined),
335
- ], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createIdentifier('value'), ts.SyntaxKind.ExclamationEqualsEqualsToken, ts.factory.createIdentifier('undefined'))),
364
+ ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('value'), undefined)
365
+ ]), undefined, undefined)
366
+ ], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createIdentifier('value'), ts.SyntaxKind.ExclamationEqualsEqualsToken, ts.factory.createIdentifier('undefined')))
336
367
  ]), ts.factory.createIdentifier('forEach')), undefined, [
337
368
  ts.factory.createArrowFunction(undefined, undefined, [
338
369
  ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createArrayBindingPattern([
339
370
  ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('key'), undefined),
340
- ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('value'), undefined),
341
- ]), undefined, undefined),
371
+ ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('value'), undefined)
372
+ ]), undefined, undefined)
342
373
  ], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
343
374
  ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseUrl'), ts.factory.createIdentifier('searchParams')), ts.factory.createIdentifier('set')), undefined, [
344
375
  ts.factory.createIdentifier('key'),
345
- ts.factory.createCallExpression(ts.factory.createIdentifier('String'), undefined, [ts.factory.createIdentifier('value')]),
346
- ])),
347
- ], false)),
376
+ ts.factory.createCallExpression(ts.factory.createIdentifier('String'), undefined, [ts.factory.createIdentifier('value')])
377
+ ]))
378
+ ], false))
348
379
  ])),
349
- ts.factory.createReturnStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseUrl'), ts.factory.createIdentifier('toString')), undefined, [])),
350
- ], true))), undefined, []), undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseUrl'), ts.factory.createIdentifier('toString')), undefined, []))),
380
+ ts.factory.createReturnStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseUrl'), ts.factory.createIdentifier('toString')), undefined, []))
381
+ ], true))), undefined, []), undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseUrl'), ts.factory.createIdentifier('toString')), undefined, [])))
351
382
  ], ts.NodeFlags.Const)),
352
383
  // Get base request options (headers, signal, credentials, etc.)
353
384
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
354
- ts.factory.createVariableDeclaration(ts.factory.createIdentifier('baseOptions'), undefined, undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createIdentifier('getBaseRequestOptions')), undefined, [])),
385
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier('baseOptions'), undefined, undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createIdentifier('getBaseRequestOptions')), undefined, []))
355
386
  ], ts.NodeFlags.Const)),
356
387
  // Build Content-Type header
357
388
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
358
- ts.factory.createVariableDeclaration(ts.factory.createIdentifier('contentType'), undefined, undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('contentType')), ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.factory.createStringLiteral('application/x-www-form-urlencoded', true)), undefined, ts.factory.createStringLiteral('application/x-www-form-urlencoded', true), undefined, ts.factory.createStringLiteral('application/json', true))),
389
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier('contentType'), undefined, undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('contentType')), ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.factory.createStringLiteral('application/x-www-form-urlencoded', true)), undefined, ts.factory.createStringLiteral('application/x-www-form-urlencoded', true), undefined, ts.factory.createStringLiteral('application/json', true)))
359
390
  ], ts.NodeFlags.Const)),
360
391
  // Merge headers: base headers, Content-Type, and request-specific headers
361
392
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
362
- ts.factory.createVariableDeclaration(ts.factory.createIdentifier('baseHeaders'), undefined, undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseOptions'), ts.factory.createIdentifier('headers')), ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), ts.factory.createIdentifier('undefined')), undefined, ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseOptions'), ts.factory.createIdentifier('headers')), undefined, ts.factory.createObjectLiteralExpression([], false))),
393
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier('baseHeaders'), undefined, undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseOptions'), ts.factory.createIdentifier('headers')), ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), ts.factory.createIdentifier('undefined')), undefined, ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseOptions'), ts.factory.createIdentifier('headers')), undefined, ts.factory.createObjectLiteralExpression([], false)))
363
394
  ], ts.NodeFlags.Const)),
364
395
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
365
396
  ts.factory.createVariableDeclaration(ts.factory.createIdentifier('headers'), undefined, undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('assign')), undefined, [
366
397
  ts.factory.createObjectLiteralExpression([], false),
367
398
  ts.factory.createIdentifier('baseHeaders'),
368
- ts.factory.createObjectLiteralExpression([
369
- ts.factory.createPropertyAssignment(ts.factory.createStringLiteral('Content-Type', true), ts.factory.createIdentifier('contentType')),
370
- ], false),
371
- ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('headers')), ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), ts.factory.createIdentifier('undefined')), undefined, ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('headers')), undefined, ts.factory.createObjectLiteralExpression([], false)),
372
- ])),
399
+ ts.factory.createObjectLiteralExpression([ts.factory.createPropertyAssignment(ts.factory.createStringLiteral('Content-Type', true), ts.factory.createIdentifier('contentType'))], false),
400
+ ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('headers')), ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), ts.factory.createIdentifier('undefined')), undefined, ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('headers')), undefined, ts.factory.createObjectLiteralExpression([], false))
401
+ ]))
373
402
  ], ts.NodeFlags.Const)),
374
403
  // Build body with form-urlencoded support
375
404
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
@@ -377,29 +406,25 @@ export class TypeScriptCodeGeneratorService {
377
406
  // Form-urlencoded: convert object to URLSearchParams
378
407
  ts.factory.createCallExpression(ts.factory.createParenthesizedExpression(ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
379
408
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
380
- ts.factory.createVariableDeclaration(ts.factory.createIdentifier('params'), undefined, undefined, ts.factory.createNewExpression(ts.factory.createIdentifier('URLSearchParams'), undefined, [])),
409
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier('params'), undefined, undefined, ts.factory.createNewExpression(ts.factory.createIdentifier('URLSearchParams'), undefined, []))
381
410
  ], ts.NodeFlags.Const)),
382
- ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('entries')), undefined, [
383
- ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('data')),
384
- ]), ts.factory.createIdentifier('forEach')), undefined, [
411
+ ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('entries')), undefined, [ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('data'))]), ts.factory.createIdentifier('forEach')), undefined, [
385
412
  ts.factory.createArrowFunction(undefined, undefined, [
386
413
  ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createArrayBindingPattern([
387
414
  ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('key'), undefined),
388
- ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('value'), undefined),
389
- ]), undefined, undefined),
415
+ ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('value'), undefined)
416
+ ]), undefined, undefined)
390
417
  ], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
391
418
  ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('params'), ts.factory.createIdentifier('set')), undefined, [
392
419
  ts.factory.createIdentifier('key'),
393
- ts.factory.createCallExpression(ts.factory.createIdentifier('String'), undefined, [ts.factory.createIdentifier('value')]),
394
- ])),
395
- ], false)),
420
+ ts.factory.createCallExpression(ts.factory.createIdentifier('String'), undefined, [ts.factory.createIdentifier('value')])
421
+ ]))
422
+ ], false))
396
423
  ])),
397
- ts.factory.createReturnStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('params'), ts.factory.createIdentifier('toString')), undefined, [])),
424
+ ts.factory.createReturnStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('params'), ts.factory.createIdentifier('toString')), undefined, []))
398
425
  ], false))), undefined, []), undefined,
399
426
  // JSON: stringify the data
400
- ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('JSON'), ts.factory.createIdentifier('stringify')), undefined, [
401
- ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('data')),
402
- ])), undefined, ts.factory.createNull())),
427
+ ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('JSON'), ts.factory.createIdentifier('stringify')), undefined, [ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('data'))])), undefined, ts.factory.createNull()))
403
428
  ], ts.NodeFlags.Const)),
404
429
  // Make fetch request: merge base options with method, headers, and body
405
430
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
@@ -411,10 +436,10 @@ export class TypeScriptCodeGeneratorService {
411
436
  ts.factory.createObjectLiteralExpression([
412
437
  ts.factory.createShorthandPropertyAssignment(ts.factory.createIdentifier('method'), undefined),
413
438
  ts.factory.createPropertyAssignment(ts.factory.createIdentifier('headers'), ts.factory.createIdentifier('headers')),
414
- ts.factory.createPropertyAssignment(ts.factory.createIdentifier('body'), ts.factory.createIdentifier('body')),
415
- ], false),
416
- ]),
417
- ]))),
439
+ ts.factory.createPropertyAssignment(ts.factory.createIdentifier('body'), ts.factory.createIdentifier('body'))
440
+ ], false)
441
+ ])
442
+ ])))
418
443
  ], ts.NodeFlags.Const)),
419
444
  // Handle response through hook (allows subclasses to intercept and modify response)
420
445
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
@@ -422,18 +447,18 @@ export class TypeScriptCodeGeneratorService {
422
447
  ts.factory.createIdentifier('rawResponse'),
423
448
  ts.factory.createIdentifier('method'),
424
449
  ts.factory.createIdentifier('path'),
425
- ts.factory.createIdentifier('options'),
426
- ]))),
450
+ ts.factory.createIdentifier('options')
451
+ ])))
427
452
  ], ts.NodeFlags.Const)),
428
453
  // Check response status
429
454
  ts.factory.createIfStatement(ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.ExclamationToken, ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('ok'))), ts.factory.createThrowStatement(ts.factory.createNewExpression(ts.factory.createIdentifier('Error'), undefined, [
430
455
  ts.factory.createTemplateExpression(ts.factory.createTemplateHead('HTTP ', 'HTTP '), [
431
456
  ts.factory.createTemplateSpan(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('status')), ts.factory.createTemplateMiddle(': ', ': ')),
432
- ts.factory.createTemplateSpan(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('statusText')), ts.factory.createTemplateTail('', '')),
433
- ]),
457
+ ts.factory.createTemplateSpan(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('statusText')), ts.factory.createTemplateTail('', ''))
458
+ ])
434
459
  ])), undefined),
435
460
  // Return parsed JSON
436
- ts.factory.createReturnStatement(ts.factory.createAwaitExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('json')), undefined, []))),
461
+ ts.factory.createReturnStatement(ts.factory.createAwaitExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('json')), undefined, [])))
437
462
  ], true));
438
463
  }
439
464
  buildClientMethods(openapi, schemas) {
@@ -479,7 +504,7 @@ export class TypeScriptCodeGeneratorService {
479
504
  // This will be handled in transformOperationName
480
505
  const modifiedSchema = {
481
506
  ...safeMethodSchema,
482
- operationId: `${operationId}_${methodLower}`,
507
+ operationId: `${operationId}_${methodLower}`
483
508
  };
484
509
  return this.buildEndpointMethod(method, path, modifiedSchema, schemas);
485
510
  }
@@ -504,7 +529,7 @@ export class TypeScriptCodeGeneratorService {
504
529
  path,
505
530
  ...(schema.tags !== undefined && { tags: schema.tags }),
506
531
  ...(schema.summary !== undefined && { summary: schema.summary }),
507
- ...(schema.description !== undefined && { description: schema.description }),
532
+ ...(schema.description !== undefined && { description: schema.description })
508
533
  };
509
534
  transformed = this.operationNameTransformer(details);
510
535
  }
@@ -543,9 +568,7 @@ export class TypeScriptCodeGeneratorService {
543
568
  }), false)
544
569
  : undefined;
545
570
  // Build request body
546
- const requestBodyExpression = hasRequestBody
547
- ? ts.factory.createIdentifier('body')
548
- : undefined;
571
+ const requestBodyExpression = hasRequestBody ? ts.factory.createIdentifier('body') : undefined;
549
572
  // Build options object for makeRequest
550
573
  const optionsProps = [];
551
574
  if (queryParamsExpression) {
@@ -561,10 +584,32 @@ export class TypeScriptCodeGeneratorService {
561
584
  const optionsExpression = ts.factory.createObjectLiteralExpression(optionsProps, false);
562
585
  // Call makeRequest
563
586
  const makeRequestCall = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createIdentifier('makeRequest')), undefined, [ts.factory.createStringLiteral(method.toUpperCase(), true), pathExpression, optionsExpression]);
564
- // Add Zod validation if we have a response schema
565
587
  if (responseSchema) {
566
- const validateCall = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(responseSchema, ts.factory.createIdentifier('parse')), undefined, [ts.factory.createAwaitExpression(makeRequestCall)]);
567
- statements.push(ts.factory.createReturnStatement(validateCall));
588
+ const schemaName = responseSchema.text;
589
+ const schemaNameLower = schemaName.charAt(0).toLowerCase() + schemaName.slice(1);
590
+ const parsedVarName = `parsed${schemaName}`;
591
+ // const response = await this.makeRequest(...)
592
+ statements.push(ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration('response', undefined, undefined, ts.factory.createAwaitExpression(makeRequestCall))], ts.NodeFlags.Const)));
593
+ // const parsed{Name} = {Schema}.safeParse(response)
594
+ statements.push(ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
595
+ ts.factory.createVariableDeclaration(parsedVarName, undefined, undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(responseSchema, ts.factory.createIdentifier('safeParse')), undefined, [
596
+ ts.factory.createIdentifier('response')
597
+ ]))
598
+ ], ts.NodeFlags.Const)));
599
+ // if (!parsed{Name}.success) { throw new ResponseValidationError<{Type}>(...) }
600
+ statements.push(ts.factory.createIfStatement(ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.ExclamationToken, ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(parsedVarName), 'success')), ts.factory.createBlock([
601
+ ts.factory.createThrowStatement(ts.factory.createNewExpression(ts.factory.createIdentifier('ResponseValidationError'), [ts.factory.createTypeReferenceNode(schemaName)], [
602
+ ts.factory.createTemplateExpression(ts.factory.createTemplateHead(`Invalid ${schemaNameLower}: `), [
603
+ ts.factory.createTemplateSpan(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(parsedVarName), 'error'), 'errors'), 'map'), undefined, [
604
+ ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'error')], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('error'), 'message'))
605
+ ]), 'join'), undefined, [ts.factory.createStringLiteral(', ', true)]), ts.factory.createTemplateTail(''))
606
+ ]),
607
+ ts.factory.createIdentifier('response'),
608
+ ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(parsedVarName), 'error')
609
+ ]))
610
+ ], true)));
611
+ // return parsed{Name}.data
612
+ statements.push(ts.factory.createReturnStatement(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(parsedVarName), 'data')));
568
613
  }
569
614
  else {
570
615
  statements.push(ts.factory.createReturnStatement(ts.factory.createAwaitExpression(makeRequestCall)));
@@ -593,7 +638,7 @@ export class TypeScriptCodeGeneratorService {
593
638
  matches.push({
594
639
  index: match.index,
595
640
  length: match[0].length,
596
- name: paramName,
641
+ name: paramName
597
642
  });
598
643
  }
599
644
  }
@@ -688,9 +733,7 @@ export class TypeScriptCodeGeneratorService {
688
733
  parameters.push(this.typeBuilder.createParameter('body', bodyType, undefined, !schema.requestBody?.required));
689
734
  }
690
735
  // Determine content type for request body
691
- const contentType = hasRequestBody && schema.requestBody?.content?.['application/x-www-form-urlencoded']
692
- ? 'application/x-www-form-urlencoded'
693
- : 'application/json';
736
+ const contentType = hasRequestBody && schema.requestBody?.content?.['application/x-www-form-urlencoded'] ? 'application/x-www-form-urlencoded' : 'application/json';
694
737
  return { parameters, pathParams, queryParams, hasRequestBody, contentType };
695
738
  }
696
739
  getParameterType(schema) {
@@ -784,9 +827,7 @@ export class TypeScriptCodeGeneratorService {
784
827
  const responseDefault = schema.responses?.['default'];
785
828
  const response = response200 ?? response201 ?? responseDefault;
786
829
  if (!response?.content?.['application/json']?.schema) {
787
- return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
788
- ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword),
789
- ]);
830
+ return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)]);
790
831
  }
791
832
  const responseSchema = response.content['application/json'].schema;
792
833
  const typeName = this.getSchemaTypeName(responseSchema, schemas);
@@ -798,26 +839,22 @@ export class TypeScriptCodeGeneratorService {
798
839
  if (schemas[sanitizedItemTypeName]) {
799
840
  // Use the type alias directly (it already uses z.infer)
800
841
  return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
801
- ts.factory.createArrayTypeNode(ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedItemTypeName), undefined)),
842
+ ts.factory.createArrayTypeNode(ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedItemTypeName), undefined))
802
843
  ]);
803
844
  }
804
845
  // If it's a primitive array, use the type name as-is
805
- return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
806
- ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(typeName), undefined),
807
- ]);
846
+ return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(typeName), undefined)]);
808
847
  }
809
848
  const sanitizedTypeName = this.typeBuilder.sanitizeIdentifier(typeName);
810
849
  // Check if it's a custom schema type (we have a type alias for it)
811
850
  if (schemas[sanitizedTypeName]) {
812
851
  // Use the type name directly (we have a type alias that already uses z.infer)
813
852
  return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
814
- ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedTypeName), undefined),
853
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedTypeName), undefined)
815
854
  ]);
816
855
  }
817
856
  // For primitive types and Record types, use the type name directly
818
- return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
819
- ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(typeName), undefined),
820
- ]);
857
+ return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(typeName), undefined)]);
821
858
  }
822
859
  buildServerConfiguration(openapi) {
823
860
  const servers = openapi.servers;
@@ -827,17 +864,13 @@ export class TypeScriptCodeGeneratorService {
827
864
  const statements = [];
828
865
  // Build server configuration array
829
866
  const serverConfigElements = servers.map((server) => {
830
- const properties = [
831
- ts.factory.createPropertyAssignment('url', ts.factory.createStringLiteral(server.url, true)),
832
- ];
867
+ const properties = [ts.factory.createPropertyAssignment('url', ts.factory.createStringLiteral(server.url, true))];
833
868
  if (server.description) {
834
869
  properties.push(ts.factory.createPropertyAssignment('description', ts.factory.createStringLiteral(server.description, true)));
835
870
  }
836
871
  if (server.variables && Object.keys(server.variables).length > 0) {
837
872
  const variableProperties = Object.entries(server.variables).map(([varName, varDef]) => {
838
- const varProps = [
839
- ts.factory.createPropertyAssignment('default', ts.factory.createStringLiteral(varDef.default, true)),
840
- ];
873
+ const varProps = [ts.factory.createPropertyAssignment('default', ts.factory.createStringLiteral(varDef.default, true))];
841
874
  if (varDef.enum && varDef.enum.length > 0) {
842
875
  varProps.push(ts.factory.createPropertyAssignment('enum', ts.factory.createArrayLiteralExpression(varDef.enum.map((val) => ts.factory.createStringLiteral(val, true)), false)));
843
876
  }
@@ -852,22 +885,20 @@ export class TypeScriptCodeGeneratorService {
852
885
  });
853
886
  // Export server configuration
854
887
  statements.push(ts.factory.createVariableStatement([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([
855
- ts.factory.createVariableDeclaration(ts.factory.createIdentifier('serverConfigurations'), undefined, undefined, ts.factory.createArrayLiteralExpression(serverConfigElements, false)),
888
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier('serverConfigurations'), undefined, undefined, ts.factory.createArrayLiteralExpression(serverConfigElements, false))
856
889
  ], ts.NodeFlags.Const)));
857
890
  // Export default base URL (first server with default variables)
858
891
  const firstServer = servers[0];
859
892
  const defaultBaseUrl = firstServer ? this.resolveServerUrl(firstServer, {}) : '/';
860
- statements.push(ts.factory.createVariableStatement([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([
861
- ts.factory.createVariableDeclaration(ts.factory.createIdentifier('defaultBaseUrl'), undefined, undefined, ts.factory.createStringLiteral(defaultBaseUrl, true)),
862
- ], ts.NodeFlags.Const)));
893
+ statements.push(ts.factory.createVariableStatement([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(ts.factory.createIdentifier('defaultBaseUrl'), undefined, undefined, ts.factory.createStringLiteral(defaultBaseUrl, true))], ts.NodeFlags.Const)));
863
894
  // Build ClientOptions type
864
895
  const optionProperties = [
865
896
  ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier('baseUrl'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)),
866
897
  ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier('serverIndex'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)),
867
898
  ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier('serverVariables'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Record'), [
868
899
  ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
869
- ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
870
- ])),
900
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
901
+ ]))
871
902
  ];
872
903
  statements.push(ts.factory.createTypeAliasDeclaration([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier('ClientOptions'), undefined, ts.factory.createTypeLiteralNode(optionProperties)));
873
904
  // Build resolveServerUrl helper function
@@ -887,14 +918,10 @@ export class TypeScriptCodeGeneratorService {
887
918
  buildResolveServerUrlFunction(servers) {
888
919
  // Build server configs array inline
889
920
  const serverConfigElements = servers.map((server) => {
890
- const properties = [
891
- ts.factory.createPropertyAssignment('url', ts.factory.createStringLiteral(server.url, true)),
892
- ];
921
+ const properties = [ts.factory.createPropertyAssignment('url', ts.factory.createStringLiteral(server.url, true))];
893
922
  if (server.variables && Object.keys(server.variables).length > 0) {
894
923
  const variableProperties = Object.entries(server.variables).map(([varName, varDef]) => {
895
- const varProps = [
896
- ts.factory.createPropertyAssignment('default', ts.factory.createStringLiteral(varDef.default, true)),
897
- ];
924
+ const varProps = [ts.factory.createPropertyAssignment('default', ts.factory.createStringLiteral(varDef.default, true))];
898
925
  if (varDef.enum && varDef.enum.length > 0) {
899
926
  varProps.push(ts.factory.createPropertyAssignment('enum', ts.factory.createArrayLiteralExpression(varDef.enum.map((val) => ts.factory.createStringLiteral(val, true)), false)));
900
927
  }
@@ -913,56 +940,47 @@ export class TypeScriptCodeGeneratorService {
913
940
  const value = ts.factory.createIdentifier('value');
914
941
  const bodyStatements = [
915
942
  // const configs = [...]
916
- ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
917
- ts.factory.createVariableDeclaration(configs, undefined, undefined, ts.factory.createArrayLiteralExpression(serverConfigElements, false)),
918
- ], ts.NodeFlags.Const)),
943
+ ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(configs, undefined, undefined, ts.factory.createArrayLiteralExpression(serverConfigElements, false))], ts.NodeFlags.Const)),
919
944
  // const idx = serverIndex ?? 0
920
945
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
921
- ts.factory.createVariableDeclaration(idx, undefined, undefined, ts.factory.createBinaryExpression(ts.factory.createIdentifier('serverIndex'), ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), ts.factory.createNumericLiteral('0'))),
946
+ ts.factory.createVariableDeclaration(idx, undefined, undefined, ts.factory.createBinaryExpression(ts.factory.createIdentifier('serverIndex'), ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), ts.factory.createNumericLiteral('0')))
922
947
  ], ts.NodeFlags.Const)),
923
948
  // if (idx < configs.length) { ... }
924
949
  ts.factory.createIfStatement(ts.factory.createBinaryExpression(idx, ts.factory.createToken(ts.SyntaxKind.LessThanToken), ts.factory.createPropertyAccessExpression(configs, ts.factory.createIdentifier('length'))), ts.factory.createBlock([
925
950
  // const config = configs[idx]
926
- ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
927
- ts.factory.createVariableDeclaration(config, undefined, undefined, ts.factory.createElementAccessExpression(configs, idx)),
928
- ], ts.NodeFlags.Const)),
951
+ ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(config, undefined, undefined, ts.factory.createElementAccessExpression(configs, idx))], ts.NodeFlags.Const)),
929
952
  // let url = config.url
930
- ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
931
- ts.factory.createVariableDeclaration(url, undefined, undefined, ts.factory.createPropertyAccessExpression(config, ts.factory.createIdentifier('url'))),
932
- ], ts.NodeFlags.Let)),
953
+ ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([ts.factory.createVariableDeclaration(url, undefined, undefined, ts.factory.createPropertyAccessExpression(config, ts.factory.createIdentifier('url')))], ts.NodeFlags.Let)),
933
954
  // if (config.variables && serverVariables) { ... }
934
955
  ts.factory.createIfStatement(ts.factory.createLogicalAnd(ts.factory.createPropertyAccessExpression(config, ts.factory.createIdentifier('variables')), ts.factory.createIdentifier('serverVariables')), ts.factory.createBlock([
935
956
  // for (const [key, value] of Object.entries(serverVariables)) { url = url.replace(...) }
936
957
  ts.factory.createForOfStatement(undefined, ts.factory.createVariableDeclarationList([
937
958
  ts.factory.createVariableDeclaration(ts.factory.createArrayBindingPattern([
938
959
  ts.factory.createBindingElement(undefined, undefined, key),
939
- ts.factory.createBindingElement(undefined, undefined, value),
940
- ]), undefined, undefined),
960
+ ts.factory.createBindingElement(undefined, undefined, value)
961
+ ]), undefined, undefined)
941
962
  ], ts.NodeFlags.Const), ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('entries')), undefined, [ts.factory.createIdentifier('serverVariables')]), ts.factory.createBlock([
942
963
  ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(url, ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(url, ts.factory.createIdentifier('replace')), undefined, [
943
964
  ts.factory.createNewExpression(ts.factory.createIdentifier('RegExp'), undefined, [
944
965
  ts.factory.createBinaryExpression(ts.factory.createBinaryExpression(ts.factory.createStringLiteral('\\{'), ts.factory.createToken(ts.SyntaxKind.PlusToken), key), ts.factory.createToken(ts.SyntaxKind.PlusToken), ts.factory.createStringLiteral('\\}')),
945
- ts.factory.createStringLiteral('g'),
966
+ ts.factory.createStringLiteral('g')
946
967
  ]),
947
- value,
948
- ]))),
949
- ], true)),
968
+ value
969
+ ])))
970
+ ], true))
950
971
  ], true)),
951
972
  // return url
952
- ts.factory.createReturnStatement(url),
973
+ ts.factory.createReturnStatement(url)
953
974
  ], true)),
954
975
  // return default (first server with defaults)
955
- ts.factory.createReturnStatement(ts.factory.createStringLiteral(servers[0] ? this.resolveServerUrl(servers[0], {}) : '/', true)),
976
+ ts.factory.createReturnStatement(ts.factory.createStringLiteral(servers[0] ? this.resolveServerUrl(servers[0], {}) : '/', true))
956
977
  ];
957
978
  return ts.factory.createFunctionDeclaration(undefined, undefined, ts.factory.createIdentifier('resolveServerUrl'), undefined, [
958
- ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('serverIndex'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createUnionTypeNode([
959
- ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
960
- ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword),
961
- ]), undefined),
979
+ ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('serverIndex'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createUnionTypeNode([ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword), ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword)]), undefined),
962
980
  ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('serverVariables'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Record'), [
963
981
  ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
964
- ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
965
- ]), ts.factory.createObjectLiteralExpression([], false)),
982
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
983
+ ]), ts.factory.createObjectLiteralExpression([], false))
966
984
  ], ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), ts.factory.createBlock(bodyStatements, true));
967
985
  }
968
986
  generateClientName(title) {
@@ -1066,9 +1084,7 @@ export class TypeScriptCodeGeneratorService {
1066
1084
  const prop = safeProperty.data;
1067
1085
  if (this.isReference(prop)) {
1068
1086
  const refSchema = this.buildFromReference(prop);
1069
- return required
1070
- ? refSchema
1071
- : ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(refSchema, ts.factory.createIdentifier('optional')), undefined, []);
1087
+ return required ? refSchema : ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(refSchema, ts.factory.createIdentifier('optional')), undefined, []);
1072
1088
  }
1073
1089
  if (prop['anyOf'] && Array.isArray(prop['anyOf']) && prop['anyOf'].length > 0) {
1074
1090
  return this.handleLogicalOperator('anyOf', prop['anyOf'], required);
@@ -1116,7 +1132,9 @@ export class TypeScriptCodeGeneratorService {
1116
1132
  else {
1117
1133
  literalValue = ts.factory.createStringLiteral(String(val), true);
1118
1134
  }
1119
- return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('literal')), undefined, [literalValue]);
1135
+ return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('literal')), undefined, [
1136
+ literalValue
1137
+ ]);
1120
1138
  });
1121
1139
  const unionExpression = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('union')), undefined, [ts.factory.createArrayLiteralExpression(literalSchemas, false)]);
1122
1140
  return required
@@ -1130,15 +1148,19 @@ export class TypeScriptCodeGeneratorService {
1130
1148
  let arraySchema = this.buildZodAST([
1131
1149
  {
1132
1150
  type: 'array',
1133
- args: [itemsSchema],
1134
- },
1151
+ args: [itemsSchema]
1152
+ }
1135
1153
  ]);
1136
1154
  // Apply array constraints
1137
1155
  if (typeof prop['minItems'] === 'number') {
1138
- arraySchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(arraySchema, ts.factory.createIdentifier('min')), undefined, [ts.factory.createNumericLiteral(String(prop['minItems']))]);
1156
+ arraySchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(arraySchema, ts.factory.createIdentifier('min')), undefined, [
1157
+ ts.factory.createNumericLiteral(String(prop['minItems']))
1158
+ ]);
1139
1159
  }
1140
1160
  if (typeof prop['maxItems'] === 'number') {
1141
- arraySchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(arraySchema, ts.factory.createIdentifier('max')), undefined, [ts.factory.createNumericLiteral(String(prop['maxItems']))]);
1161
+ arraySchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(arraySchema, ts.factory.createIdentifier('max')), undefined, [
1162
+ ts.factory.createNumericLiteral(String(prop['maxItems']))
1163
+ ]);
1142
1164
  }
1143
1165
  return required
1144
1166
  ? arraySchema
@@ -1156,9 +1178,9 @@ export class TypeScriptCodeGeneratorService {
1156
1178
  args: [
1157
1179
  ts.factory.createObjectLiteralExpression(propertiesEntries.map(([name, propValue]) => {
1158
1180
  return ts.factory.createPropertyAssignment(ts.factory.createIdentifier(name), this.buildProperty(propValue, propRequired.includes(name)));
1159
- }), true),
1160
- ],
1161
- },
1181
+ }), true)
1182
+ ]
1183
+ }
1162
1184
  ]);
1163
1185
  // Apply object constraints
1164
1186
  let constrainedSchema = objectSchema;
@@ -1166,16 +1188,16 @@ export class TypeScriptCodeGeneratorService {
1166
1188
  constrainedSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(constrainedSchema, ts.factory.createIdentifier('refine')), undefined, [
1167
1189
  ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'obj', undefined, undefined, undefined)], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('keys')), undefined, [ts.factory.createIdentifier('obj')]), ts.factory.createIdentifier('length')), ts.factory.createToken(ts.SyntaxKind.GreaterThanEqualsToken), ts.factory.createNumericLiteral(String(prop['minProperties'])))),
1168
1190
  ts.factory.createObjectLiteralExpression([
1169
- ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Object must have at least ${String(prop['minProperties'])} properties`)),
1170
- ]),
1191
+ ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Object must have at least ${String(prop['minProperties'])} properties`))
1192
+ ])
1171
1193
  ]);
1172
1194
  }
1173
1195
  if (typeof prop['maxProperties'] === 'number') {
1174
1196
  constrainedSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(constrainedSchema, ts.factory.createIdentifier('refine')), undefined, [
1175
1197
  ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'obj', undefined, undefined, undefined)], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('keys')), undefined, [ts.factory.createIdentifier('obj')]), ts.factory.createIdentifier('length')), ts.factory.createToken(ts.SyntaxKind.LessThanEqualsToken), ts.factory.createNumericLiteral(String(prop['maxProperties'])))),
1176
1198
  ts.factory.createObjectLiteralExpression([
1177
- ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Object must have at most ${String(prop['maxProperties'])} properties`)),
1178
- ]),
1199
+ ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Object must have at most ${String(prop['maxProperties'])} properties`))
1200
+ ])
1179
1201
  ]);
1180
1202
  }
1181
1203
  return required
@@ -1185,31 +1207,30 @@ export class TypeScriptCodeGeneratorService {
1185
1207
  return this.buildZodAST([
1186
1208
  {
1187
1209
  type: 'record',
1188
- args: [this.buildZodAST(['string']), this.buildZodAST(['unknown'])],
1189
- },
1210
+ args: [this.buildZodAST(['string']), this.buildZodAST(['unknown'])]
1211
+ }
1190
1212
  ]);
1191
1213
  }
1192
1214
  case 'integer': {
1193
1215
  let numberSchema = this.buildZodAST(['number', 'int']);
1194
1216
  // Apply number constraints
1195
1217
  if (prop['minimum'] !== undefined && typeof prop['minimum'] === 'number') {
1196
- const minValue = prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean'
1197
- ? prop['minimum'] + 1
1198
- : prop['minimum'];
1218
+ const minValue = prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean' ? prop['minimum'] + 1 : prop['minimum'];
1199
1219
  numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier(prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean' ? 'gt' : 'gte')), undefined, [ts.factory.createNumericLiteral(String(minValue))]);
1200
1220
  }
1201
1221
  if (prop['maximum'] !== undefined && typeof prop['maximum'] === 'number') {
1202
- const maxValue = prop['exclusiveMaximum'] && typeof prop['exclusiveMaximum'] === 'boolean'
1203
- ? prop['maximum'] - 1
1204
- : prop['maximum'];
1222
+ const maxValue = prop['exclusiveMaximum'] && typeof prop['exclusiveMaximum'] === 'boolean' ? prop['maximum'] - 1 : prop['maximum'];
1205
1223
  numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier(prop['exclusiveMaximum'] ? 'lt' : 'lte')), undefined, [ts.factory.createNumericLiteral(String(maxValue))]);
1206
1224
  }
1207
1225
  if (typeof prop['multipleOf'] === 'number') {
1208
1226
  const refineFunction = ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'val', undefined, undefined, undefined)], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createBinaryExpression(ts.factory.createIdentifier('val'), ts.factory.createToken(ts.SyntaxKind.PercentToken), ts.factory.createNumericLiteral(String(prop['multipleOf']))), ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.factory.createNumericLiteral('0')));
1209
1227
  const refineOptions = ts.factory.createObjectLiteralExpression([
1210
- ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Number must be a multiple of ${String(prop['multipleOf'])}`)),
1228
+ ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Number must be a multiple of ${String(prop['multipleOf'])}`))
1229
+ ]);
1230
+ numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier('refine')), undefined, [
1231
+ refineFunction,
1232
+ refineOptions
1211
1233
  ]);
1212
- numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier('refine')), undefined, [refineFunction, refineOptions]);
1213
1234
  }
1214
1235
  return required
1215
1236
  ? numberSchema
@@ -1219,23 +1240,22 @@ export class TypeScriptCodeGeneratorService {
1219
1240
  let numberSchema = this.buildZodAST(['number']);
1220
1241
  // Apply number constraints
1221
1242
  if (prop['minimum'] !== undefined && typeof prop['minimum'] === 'number') {
1222
- const minValue = prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean'
1223
- ? prop['minimum'] + 1
1224
- : prop['minimum'];
1243
+ const minValue = prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean' ? prop['minimum'] + 1 : prop['minimum'];
1225
1244
  numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier(prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean' ? 'gt' : 'gte')), undefined, [ts.factory.createNumericLiteral(String(minValue))]);
1226
1245
  }
1227
1246
  if (prop['maximum'] !== undefined && typeof prop['maximum'] === 'number') {
1228
- const maxValue = prop['exclusiveMaximum'] && typeof prop['exclusiveMaximum'] === 'boolean'
1229
- ? prop['maximum'] - 1
1230
- : prop['maximum'];
1247
+ const maxValue = prop['exclusiveMaximum'] && typeof prop['exclusiveMaximum'] === 'boolean' ? prop['maximum'] - 1 : prop['maximum'];
1231
1248
  numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier(prop['exclusiveMaximum'] ? 'lt' : 'lte')), undefined, [ts.factory.createNumericLiteral(String(maxValue))]);
1232
1249
  }
1233
1250
  if (typeof prop['multipleOf'] === 'number') {
1234
1251
  const refineFunction = ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'val', undefined, undefined, undefined)], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createBinaryExpression(ts.factory.createIdentifier('val'), ts.factory.createToken(ts.SyntaxKind.PercentToken), ts.factory.createNumericLiteral(String(prop['multipleOf']))), ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.factory.createNumericLiteral('0')));
1235
1252
  const refineOptions = ts.factory.createObjectLiteralExpression([
1236
- ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Number must be a multiple of ${String(prop['multipleOf'])}`)),
1253
+ ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Number must be a multiple of ${String(prop['multipleOf'])}`))
1254
+ ]);
1255
+ numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier('refine')), undefined, [
1256
+ refineFunction,
1257
+ refineOptions
1237
1258
  ]);
1238
- numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier('refine')), undefined, [refineFunction, refineOptions]);
1239
1259
  }
1240
1260
  return required
1241
1261
  ? numberSchema
@@ -1270,22 +1290,26 @@ export class TypeScriptCodeGeneratorService {
1270
1290
  }
1271
1291
  // Apply string constraints
1272
1292
  if (typeof prop['minLength'] === 'number') {
1273
- stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('min')), undefined, [ts.factory.createNumericLiteral(String(prop['minLength']))]);
1293
+ stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('min')), undefined, [
1294
+ ts.factory.createNumericLiteral(String(prop['minLength']))
1295
+ ]);
1274
1296
  }
1275
1297
  if (typeof prop['maxLength'] === 'number') {
1276
- stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('max')), undefined, [ts.factory.createNumericLiteral(String(prop['maxLength']))]);
1298
+ stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('max')), undefined, [
1299
+ ts.factory.createNumericLiteral(String(prop['maxLength']))
1300
+ ]);
1277
1301
  }
1278
1302
  if (prop['pattern'] && typeof prop['pattern'] === 'string') {
1279
1303
  stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('regex')), undefined, [
1280
- ts.factory.createNewExpression(ts.factory.createIdentifier('RegExp'), undefined, [
1281
- ts.factory.createStringLiteral(prop['pattern'], true),
1282
- ]),
1304
+ ts.factory.createNewExpression(ts.factory.createIdentifier('RegExp'), undefined, [ts.factory.createStringLiteral(prop['pattern'], true)])
1283
1305
  ]);
1284
1306
  }
1285
1307
  // Apply default value if not required
1286
1308
  if (!required && prop['default'] !== undefined) {
1287
1309
  const defaultValue = this.buildDefaultValue(prop['default']);
1288
- stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('default')), undefined, [defaultValue]);
1310
+ stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('default')), undefined, [
1311
+ defaultValue
1312
+ ]);
1289
1313
  }
1290
1314
  return required
1291
1315
  ? stringSchema
@@ -1295,12 +1319,10 @@ export class TypeScriptCodeGeneratorService {
1295
1319
  let booleanSchema = this.buildZodAST(['boolean']);
1296
1320
  // Apply default value if not required
1297
1321
  if (!required && prop['default'] !== undefined) {
1298
- const defaultValue = typeof prop['default'] === 'boolean'
1299
- ? prop['default']
1300
- ? ts.factory.createTrue()
1301
- : ts.factory.createFalse()
1302
- : ts.factory.createFalse();
1303
- booleanSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(booleanSchema, ts.factory.createIdentifier('default')), undefined, [defaultValue]);
1322
+ const defaultValue = typeof prop['default'] === 'boolean' ? (prop['default'] ? ts.factory.createTrue() : ts.factory.createFalse()) : ts.factory.createFalse();
1323
+ booleanSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(booleanSchema, ts.factory.createIdentifier('default')), undefined, [
1324
+ defaultValue
1325
+ ]);
1304
1326
  }
1305
1327
  return required
1306
1328
  ? booleanSchema
@@ -1352,7 +1374,9 @@ export class TypeScriptCodeGeneratorService {
1352
1374
  case 'anyOf':
1353
1375
  case 'oneOf': {
1354
1376
  const unionSchemas = schemas.map((schema) => this.buildSchemaFromLogicalOperator(schema));
1355
- return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('union')), undefined, [ts.factory.createArrayLiteralExpression(unionSchemas, false)]);
1377
+ return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('union')), undefined, [
1378
+ ts.factory.createArrayLiteralExpression(unionSchemas, false)
1379
+ ]);
1356
1380
  }
1357
1381
  case 'allOf': {
1358
1382
  if (schemas.length === 0) {
@@ -1367,10 +1391,12 @@ export class TypeScriptCodeGeneratorService {
1367
1391
  case 'not': {
1368
1392
  const notSchema = this.buildSchemaFromLogicalOperator(schemas[0]);
1369
1393
  return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('any')), ts.factory.createIdentifier('refine')), undefined, [
1370
- ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'val', undefined, undefined, undefined)], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.ExclamationToken, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(notSchema, ts.factory.createIdentifier('safeParse')), undefined, [ts.factory.createIdentifier('val')]))),
1394
+ ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'val', undefined, undefined, undefined)], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.ExclamationToken, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(notSchema, ts.factory.createIdentifier('safeParse')), undefined, [
1395
+ ts.factory.createIdentifier('val')
1396
+ ]))),
1371
1397
  ts.factory.createObjectLiteralExpression([
1372
- ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral('Value must not match the excluded schema')),
1373
- ]),
1398
+ ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral('Value must not match the excluded schema'))
1399
+ ])
1374
1400
  ]);
1375
1401
  }
1376
1402
  default:
@@ -1419,16 +1445,16 @@ export class TypeScriptCodeGeneratorService {
1419
1445
  args: [
1420
1446
  ts.factory.createObjectLiteralExpression(properties.map(([name, property]) => {
1421
1447
  return ts.factory.createPropertyAssignment(ts.factory.createIdentifier(name), this.buildSchemaFromLogicalOperator(property));
1422
- }), true),
1423
- ],
1424
- },
1448
+ }), true)
1449
+ ]
1450
+ }
1425
1451
  ]);
1426
1452
  }
1427
1453
  return this.buildZodAST([
1428
1454
  {
1429
1455
  type: 'record',
1430
- args: [this.buildZodAST(['string']), this.buildZodAST(['unknown'])],
1431
- },
1456
+ args: [this.buildZodAST(['string']), this.buildZodAST(['unknown'])]
1457
+ }
1432
1458
  ]);
1433
1459
  }
1434
1460
  buildArrayTypeFromSchema(schemaObj) {
@@ -1436,8 +1462,8 @@ export class TypeScriptCodeGeneratorService {
1436
1462
  return this.buildZodAST([
1437
1463
  {
1438
1464
  type: 'array',
1439
- args: [this.buildSchemaFromLogicalOperator(schemaObj.items)],
1440
- },
1465
+ args: [this.buildSchemaFromLogicalOperator(schemaObj.items)]
1466
+ }
1441
1467
  ]);
1442
1468
  }
1443
1469
  return this.buildZodAST(['array']);
@@ -1457,7 +1483,7 @@ export class TypeScriptCodeGeneratorService {
1457
1483
  if (this.isCircularReference(refName)) {
1458
1484
  // Generate: z.lazy(() => RefSchema)
1459
1485
  return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('lazy')), undefined, [
1460
- ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createIdentifier(sanitizedRefName)),
1486
+ ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createIdentifier(sanitizedRefName))
1461
1487
  ]);
1462
1488
  }
1463
1489
  return ts.factory.createIdentifier(sanitizedRefName);
@@ -1475,9 +1501,7 @@ export class TypeScriptCodeGeneratorService {
1475
1501
  }
1476
1502
  // Case 2: Reference to a schema that's part of a circular dependency chain
1477
1503
  // and we're currently building a schema that's also in that chain
1478
- if (this.circularSchemas.has(refName) &&
1479
- this.currentSchemaName !== null &&
1480
- this.circularSchemas.has(this.currentSchemaName)) {
1504
+ if (this.circularSchemas.has(refName) && this.currentSchemaName !== null && this.circularSchemas.has(this.currentSchemaName)) {
1481
1505
  return true;
1482
1506
  }
1483
1507
  return false;
@@ -1492,11 +1516,7 @@ export class TypeScriptCodeGeneratorService {
1492
1516
  // Build dependency graph
1493
1517
  const graph = new Map();
1494
1518
  for (const [name, schema] of Object.entries(schemas)) {
1495
- const dependencies = jp
1496
- .query(schema, '$..["$ref"]')
1497
- .filter((ref) => ref.startsWith('#/components/schemas/'))
1498
- .map((ref) => ref.replace('#/components/schemas/', ''))
1499
- .filter((dep) => dep in schemas);
1519
+ const dependencies = this.extractSchemaReferences(schema).filter((dep) => dep in schemas);
1500
1520
  graph.set(name, dependencies);
1501
1521
  }
1502
1522
  // Tarjan's algorithm for finding SCCs
@@ -1574,10 +1594,7 @@ export class TypeScriptCodeGeneratorService {
1574
1594
  }
1575
1595
  visiting.add(name);
1576
1596
  const schema = schemas[name];
1577
- const dependencies = jp
1578
- .query(schema, '$..["$ref"]')
1579
- .filter((ref) => ref.startsWith('#/components/schemas/'))
1580
- .map((ref) => ref.replace('#/components/schemas/', ''));
1597
+ const dependencies = this.extractSchemaReferences(schema);
1581
1598
  for (const dep of dependencies) {
1582
1599
  if (schemas[dep]) {
1583
1600
  visit(dep);