zod-codegen 1.0.3 → 1.1.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.
@@ -40,14 +40,14 @@ export class TypeScriptCodeGeneratorService {
40
40
  buildAST(openapi) {
41
41
  const imports = this.importBuilder.buildImports();
42
42
  const schemas = this.buildSchemas(openapi);
43
+ const serverConfig = this.buildServerConfiguration(openapi);
43
44
  const clientClass = this.buildClientClass(openapi, schemas);
44
- const baseUrlConstant = this.buildBaseUrlConstant(openapi);
45
45
  return [
46
46
  this.createComment('Imports'),
47
47
  ...imports,
48
48
  this.createComment('Components schemas'),
49
49
  ...Object.values(schemas),
50
- ...baseUrlConstant,
50
+ ...serverConfig,
51
51
  this.createComment('Client class'),
52
52
  clientClass,
53
53
  ];
@@ -73,24 +73,54 @@ export class TypeScriptCodeGeneratorService {
73
73
  const methods = this.buildClientMethods(openapi, schemas);
74
74
  return ts.factory.createClassDeclaration([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier(clientName), undefined, undefined, [
75
75
  this.typeBuilder.createProperty('#baseUrl', 'string', true),
76
- this.buildConstructor(),
76
+ this.buildConstructor(openapi),
77
+ this.buildGetBaseRequestOptionsMethod(),
77
78
  this.buildHttpRequestMethod(),
78
79
  ...methods,
79
80
  ]);
80
81
  }
81
- buildConstructor() {
82
- return ts.factory.createConstructorDeclaration(undefined, [
83
- this.typeBuilder.createParameter('baseUrl', 'string', ts.factory.createIdentifier('defaultBaseUrl')),
84
- this.typeBuilder.createParameter('_', 'unknown', undefined, true),
85
- ], ts.factory.createBlock([
86
- 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'))),
87
- ], true));
82
+ buildConstructor(openapi) {
83
+ const hasServers = openapi.servers && openapi.servers.length > 0;
84
+ if (hasServers) {
85
+ // Options-based constructor
86
+ return ts.factory.createConstructorDeclaration(undefined, [
87
+ ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('options'), undefined, ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('ClientOptions'), undefined), undefined),
88
+ ], ts.factory.createBlock([
89
+ ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
90
+ 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, [
91
+ ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('serverIndex')),
92
+ ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('serverVariables')),
93
+ ]))),
94
+ ], ts.NodeFlags.Const)),
95
+ 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'))),
96
+ ], true));
97
+ }
98
+ else {
99
+ // Fallback: simple baseUrl parameter
100
+ return ts.factory.createConstructorDeclaration(undefined, [
101
+ this.typeBuilder.createParameter('baseUrl', 'string', ts.factory.createStringLiteral('/', true)),
102
+ this.typeBuilder.createParameter('_', 'unknown', undefined, true),
103
+ ], ts.factory.createBlock([
104
+ 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'))),
105
+ ], true));
106
+ }
107
+ }
108
+ buildGetBaseRequestOptionsMethod() {
109
+ return ts.factory.createMethodDeclaration([ts.factory.createToken(ts.SyntaxKind.ProtectedKeyword)], undefined, ts.factory.createIdentifier('getBaseRequestOptions'), undefined, undefined, [], ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Partial'), [
110
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Omit'), [
111
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('RequestInit'), undefined),
112
+ ts.factory.createUnionTypeNode([
113
+ ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral('method', true)),
114
+ ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral('body', true)),
115
+ ]),
116
+ ]),
117
+ ]), ts.factory.createBlock([ts.factory.createReturnStatement(ts.factory.createObjectLiteralExpression([], false))], true));
88
118
  }
89
119
  buildHttpRequestMethod() {
90
120
  return ts.factory.createMethodDeclaration([ts.factory.createToken(ts.SyntaxKind.AsyncKeyword)], undefined, ts.factory.createPrivateIdentifier('#makeRequest'), undefined, [this.typeBuilder.createGenericType('T')], [
91
121
  this.typeBuilder.createParameter('method', 'string'),
92
122
  this.typeBuilder.createParameter('path', 'string'),
93
- this.typeBuilder.createParameter('options', '{params?: Record<string, string | number | boolean>; data?: unknown; contentType?: string}', ts.factory.createObjectLiteralExpression([], false), false),
123
+ this.typeBuilder.createParameter('options', '{params?: Record<string, string | number | boolean>; data?: unknown; contentType?: string; headers?: Record<string, string>}', ts.factory.createObjectLiteralExpression([], false), false),
94
124
  ], ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
95
125
  ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('T'), undefined),
96
126
  ]), ts.factory.createBlock([
@@ -132,11 +162,27 @@ export class TypeScriptCodeGeneratorService {
132
162
  ts.factory.createIdentifier('baseUrl'),
133
163
  ]), ts.factory.createIdentifier('toString')), undefined, []))),
134
164
  ], ts.NodeFlags.Const)),
135
- // Build headers with dynamic Content-Type
165
+ // Get base request options (headers, signal, credentials, etc.)
136
166
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
137
- ts.factory.createVariableDeclaration(ts.factory.createIdentifier('headers'), undefined, undefined, ts.factory.createObjectLiteralExpression([
138
- ts.factory.createPropertyAssignment(ts.factory.createStringLiteral('Content-Type', true), 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))),
139
- ], true)),
167
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier('baseOptions'), undefined, undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createIdentifier('getBaseRequestOptions')), undefined, [])),
168
+ ], ts.NodeFlags.Const)),
169
+ // Build Content-Type header
170
+ ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
171
+ 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))),
172
+ ], ts.NodeFlags.Const)),
173
+ // Merge headers: base headers, Content-Type, and request-specific headers
174
+ ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
175
+ 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))),
176
+ ], ts.NodeFlags.Const)),
177
+ ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
178
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier('headers'), undefined, undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('assign')), undefined, [
179
+ ts.factory.createObjectLiteralExpression([], false),
180
+ ts.factory.createIdentifier('baseHeaders'),
181
+ ts.factory.createObjectLiteralExpression([
182
+ ts.factory.createPropertyAssignment(ts.factory.createStringLiteral('Content-Type', true), ts.factory.createIdentifier('contentType')),
183
+ ], false),
184
+ 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)),
185
+ ])),
140
186
  ], ts.NodeFlags.Const)),
141
187
  // Build body with form-urlencoded support
142
188
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
@@ -168,15 +214,19 @@ export class TypeScriptCodeGeneratorService {
168
214
  ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('data')),
169
215
  ])), undefined, ts.factory.createNull())),
170
216
  ], ts.NodeFlags.Const)),
171
- // Make fetch request
217
+ // Make fetch request: merge base options with method, headers, and body
172
218
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
173
219
  ts.factory.createVariableDeclaration(ts.factory.createIdentifier('response'), undefined, undefined, ts.factory.createAwaitExpression(ts.factory.createCallExpression(ts.factory.createIdentifier('fetch'), undefined, [
174
220
  ts.factory.createIdentifier('url'),
175
- ts.factory.createObjectLiteralExpression([
176
- ts.factory.createShorthandPropertyAssignment(ts.factory.createIdentifier('method'), undefined),
177
- ts.factory.createPropertyAssignment(ts.factory.createIdentifier('headers'), ts.factory.createIdentifier('headers')),
178
- ts.factory.createPropertyAssignment(ts.factory.createIdentifier('body'), ts.factory.createIdentifier('body')),
179
- ], true),
221
+ ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('assign')), undefined, [
222
+ ts.factory.createObjectLiteralExpression([], false),
223
+ ts.factory.createIdentifier('baseOptions'),
224
+ ts.factory.createObjectLiteralExpression([
225
+ ts.factory.createShorthandPropertyAssignment(ts.factory.createIdentifier('method'), undefined),
226
+ ts.factory.createPropertyAssignment(ts.factory.createIdentifier('headers'), ts.factory.createIdentifier('headers')),
227
+ ts.factory.createPropertyAssignment(ts.factory.createIdentifier('body'), ts.factory.createIdentifier('body')),
228
+ ], false),
229
+ ]),
180
230
  ]))),
181
231
  ], ts.NodeFlags.Const)),
182
232
  // Check response status
@@ -462,16 +512,151 @@ export class TypeScriptCodeGeneratorService {
462
512
  ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(typeName), undefined),
463
513
  ]);
464
514
  }
465
- buildBaseUrlConstant(openapi) {
466
- const baseUrl = openapi.servers?.[0]?.url;
467
- if (!baseUrl) {
515
+ buildServerConfiguration(openapi) {
516
+ const servers = openapi.servers;
517
+ if (!servers || servers.length === 0) {
468
518
  return [];
469
519
  }
470
- return [
520
+ const statements = [];
521
+ // Build server configuration array
522
+ const serverConfigElements = servers.map((server) => {
523
+ const properties = [
524
+ ts.factory.createPropertyAssignment('url', ts.factory.createStringLiteral(server.url, true)),
525
+ ];
526
+ if (server.description) {
527
+ properties.push(ts.factory.createPropertyAssignment('description', ts.factory.createStringLiteral(server.description, true)));
528
+ }
529
+ if (server.variables && Object.keys(server.variables).length > 0) {
530
+ const variableProperties = Object.entries(server.variables).map(([varName, varDef]) => {
531
+ const varProps = [
532
+ ts.factory.createPropertyAssignment('default', ts.factory.createStringLiteral(varDef.default, true)),
533
+ ];
534
+ if (varDef.enum && varDef.enum.length > 0) {
535
+ varProps.push(ts.factory.createPropertyAssignment('enum', ts.factory.createArrayLiteralExpression(varDef.enum.map((val) => ts.factory.createStringLiteral(val, true)), false)));
536
+ }
537
+ if (varDef.description) {
538
+ varProps.push(ts.factory.createPropertyAssignment('description', ts.factory.createStringLiteral(varDef.description, true)));
539
+ }
540
+ return ts.factory.createPropertyAssignment(varName, ts.factory.createObjectLiteralExpression(varProps, true));
541
+ });
542
+ properties.push(ts.factory.createPropertyAssignment('variables', ts.factory.createObjectLiteralExpression(variableProperties, true)));
543
+ }
544
+ return ts.factory.createObjectLiteralExpression(properties, true);
545
+ });
546
+ // Export server configuration
547
+ statements.push(ts.factory.createVariableStatement([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([
548
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier('serverConfigurations'), undefined, undefined, ts.factory.createArrayLiteralExpression(serverConfigElements, false)),
549
+ ], ts.NodeFlags.Const)));
550
+ // Export default base URL (first server with default variables)
551
+ const firstServer = servers[0];
552
+ const defaultBaseUrl = firstServer ? this.resolveServerUrl(firstServer, {}) : '/';
553
+ statements.push(ts.factory.createVariableStatement([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([
554
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier('defaultBaseUrl'), undefined, undefined, ts.factory.createStringLiteral(defaultBaseUrl, true)),
555
+ ], ts.NodeFlags.Const)));
556
+ // Build ClientOptions type
557
+ const optionProperties = [
558
+ ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier('baseUrl'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)),
559
+ ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier('serverIndex'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)),
560
+ ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier('serverVariables'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Record'), [
561
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
562
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
563
+ ])),
564
+ ];
565
+ statements.push(ts.factory.createTypeAliasDeclaration([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier('ClientOptions'), undefined, ts.factory.createTypeLiteralNode(optionProperties)));
566
+ // Build resolveServerUrl helper function
567
+ statements.push(this.buildResolveServerUrlFunction(servers));
568
+ return statements;
569
+ }
570
+ resolveServerUrl(server, variables) {
571
+ let url = server.url;
572
+ if (server.variables) {
573
+ for (const [varName, varDef] of Object.entries(server.variables)) {
574
+ const value = variables[varName] ?? varDef.default;
575
+ url = url.replace(`{${varName}}`, value);
576
+ }
577
+ }
578
+ return url;
579
+ }
580
+ buildResolveServerUrlFunction(servers) {
581
+ // Build server configs array inline
582
+ const serverConfigElements = servers.map((server) => {
583
+ const properties = [
584
+ ts.factory.createPropertyAssignment('url', ts.factory.createStringLiteral(server.url, true)),
585
+ ];
586
+ if (server.variables && Object.keys(server.variables).length > 0) {
587
+ const variableProperties = Object.entries(server.variables).map(([varName, varDef]) => {
588
+ const varProps = [
589
+ ts.factory.createPropertyAssignment('default', ts.factory.createStringLiteral(varDef.default, true)),
590
+ ];
591
+ if (varDef.enum && varDef.enum.length > 0) {
592
+ varProps.push(ts.factory.createPropertyAssignment('enum', ts.factory.createArrayLiteralExpression(varDef.enum.map((val) => ts.factory.createStringLiteral(val, true)), false)));
593
+ }
594
+ return ts.factory.createPropertyAssignment(varName, ts.factory.createObjectLiteralExpression(varProps, true));
595
+ });
596
+ properties.push(ts.factory.createPropertyAssignment('variables', ts.factory.createObjectLiteralExpression(variableProperties, true)));
597
+ }
598
+ return ts.factory.createObjectLiteralExpression(properties, true);
599
+ });
600
+ // Build function body - simplified version
601
+ const idx = ts.factory.createIdentifier('idx');
602
+ const configs = ts.factory.createIdentifier('configs');
603
+ const config = ts.factory.createIdentifier('config');
604
+ const url = ts.factory.createIdentifier('url');
605
+ const key = ts.factory.createIdentifier('key');
606
+ const value = ts.factory.createIdentifier('value');
607
+ const bodyStatements = [
608
+ // const configs = [...]
609
+ ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
610
+ ts.factory.createVariableDeclaration(configs, undefined, undefined, ts.factory.createArrayLiteralExpression(serverConfigElements, false)),
611
+ ], ts.NodeFlags.Const)),
612
+ // const idx = serverIndex ?? 0
471
613
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
472
- ts.factory.createVariableDeclaration(ts.factory.createIdentifier('defaultBaseUrl'), undefined, undefined, ts.factory.createStringLiteral(baseUrl, true)),
614
+ ts.factory.createVariableDeclaration(idx, undefined, undefined, ts.factory.createBinaryExpression(ts.factory.createIdentifier('serverIndex'), ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken), ts.factory.createNumericLiteral('0'))),
473
615
  ], ts.NodeFlags.Const)),
616
+ // if (idx < configs.length) { ... }
617
+ ts.factory.createIfStatement(ts.factory.createBinaryExpression(idx, ts.factory.createToken(ts.SyntaxKind.LessThanToken), ts.factory.createPropertyAccessExpression(configs, ts.factory.createIdentifier('length'))), ts.factory.createBlock([
618
+ // const config = configs[idx]
619
+ ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
620
+ ts.factory.createVariableDeclaration(config, undefined, undefined, ts.factory.createElementAccessExpression(configs, idx)),
621
+ ], ts.NodeFlags.Const)),
622
+ // let url = config.url
623
+ ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
624
+ ts.factory.createVariableDeclaration(url, undefined, undefined, ts.factory.createPropertyAccessExpression(config, ts.factory.createIdentifier('url'))),
625
+ ], ts.NodeFlags.Let)),
626
+ // if (config.variables && serverVariables) { ... }
627
+ ts.factory.createIfStatement(ts.factory.createLogicalAnd(ts.factory.createPropertyAccessExpression(config, ts.factory.createIdentifier('variables')), ts.factory.createIdentifier('serverVariables')), ts.factory.createBlock([
628
+ // for (const [key, value] of Object.entries(serverVariables)) { url = url.replace(...) }
629
+ ts.factory.createForOfStatement(undefined, ts.factory.createVariableDeclarationList([
630
+ ts.factory.createVariableDeclaration(ts.factory.createArrayBindingPattern([
631
+ ts.factory.createBindingElement(undefined, undefined, key),
632
+ ts.factory.createBindingElement(undefined, undefined, value),
633
+ ]), undefined, undefined),
634
+ ], 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([
635
+ 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, [
636
+ ts.factory.createNewExpression(ts.factory.createIdentifier('RegExp'), undefined, [
637
+ 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('\\}')),
638
+ ts.factory.createStringLiteral('g'),
639
+ ]),
640
+ value,
641
+ ]))),
642
+ ], true)),
643
+ ], true)),
644
+ // return url
645
+ ts.factory.createReturnStatement(url),
646
+ ], true)),
647
+ // return default (first server with defaults)
648
+ ts.factory.createReturnStatement(ts.factory.createStringLiteral(servers[0] ? this.resolveServerUrl(servers[0], {}) : '/', true)),
474
649
  ];
650
+ return ts.factory.createFunctionDeclaration(undefined, undefined, ts.factory.createIdentifier('resolveServerUrl'), undefined, [
651
+ ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('serverIndex'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createUnionTypeNode([
652
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
653
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword),
654
+ ]), undefined),
655
+ ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createIdentifier('serverVariables'), ts.factory.createToken(ts.SyntaxKind.QuestionToken), ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Record'), [
656
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
657
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
658
+ ]), ts.factory.createObjectLiteralExpression([], false)),
659
+ ], ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), ts.factory.createBlock(bodyStatements, true));
475
660
  }
476
661
  generateClientName(title) {
477
662
  return title
@@ -523,26 +708,45 @@ export class TypeScriptCodeGeneratorService {
523
708
  }
524
709
  // Handle enum
525
710
  if (prop['enum'] && Array.isArray(prop['enum']) && prop['enum'].length > 0) {
526
- const enumValues = prop['enum'].map((val) => {
527
- if (typeof val === 'string') {
528
- return ts.factory.createStringLiteral(val, true);
529
- }
530
- if (typeof val === 'number') {
531
- // Handle negative numbers correctly
532
- if (val < 0) {
533
- return ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, ts.factory.createNumericLiteral(String(Math.abs(val))));
711
+ // Check if all enum values are strings (z.enum only works with strings)
712
+ const allStrings = prop['enum'].every((val) => typeof val === 'string');
713
+ if (allStrings) {
714
+ // Use z.enum() for string enums
715
+ const enumValues = prop['enum'].map((val) => ts.factory.createStringLiteral(val, true));
716
+ const enumExpression = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('enum')), undefined, [ts.factory.createArrayLiteralExpression(enumValues, false)]);
717
+ return required
718
+ ? enumExpression
719
+ : ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(enumExpression, ts.factory.createIdentifier('optional')), undefined, []);
720
+ }
721
+ else {
722
+ // Use z.union([z.literal(...), ...]) for numeric/boolean/mixed enums
723
+ const literalSchemas = prop['enum'].map((val) => {
724
+ let literalValue;
725
+ if (typeof val === 'string') {
726
+ literalValue = ts.factory.createStringLiteral(val, true);
534
727
  }
535
- return ts.factory.createNumericLiteral(String(val));
536
- }
537
- if (typeof val === 'boolean') {
538
- return val ? ts.factory.createTrue() : ts.factory.createFalse();
539
- }
540
- return ts.factory.createStringLiteral(String(val), true);
541
- });
542
- const enumExpression = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('enum')), undefined, [ts.factory.createArrayLiteralExpression(enumValues, false)]);
543
- return required
544
- ? enumExpression
545
- : ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(enumExpression, ts.factory.createIdentifier('optional')), undefined, []);
728
+ else if (typeof val === 'number') {
729
+ // Handle negative numbers correctly
730
+ if (val < 0) {
731
+ literalValue = ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, ts.factory.createNumericLiteral(String(Math.abs(val))));
732
+ }
733
+ else {
734
+ literalValue = ts.factory.createNumericLiteral(String(val));
735
+ }
736
+ }
737
+ else if (typeof val === 'boolean') {
738
+ literalValue = val ? ts.factory.createTrue() : ts.factory.createFalse();
739
+ }
740
+ else {
741
+ literalValue = ts.factory.createStringLiteral(String(val), true);
742
+ }
743
+ return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('literal')), undefined, [literalValue]);
744
+ });
745
+ const unionExpression = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('union')), undefined, [ts.factory.createArrayLiteralExpression(literalSchemas, false)]);
746
+ return required
747
+ ? unionExpression
748
+ : ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(unionExpression, ts.factory.createIdentifier('optional')), undefined, []);
749
+ }
546
750
  }
547
751
  switch (prop['type']) {
548
752
  case 'array': {
@@ -54,7 +54,7 @@ const ServerVariable = z.object({
54
54
  enum: z.array(z.string()).optional(),
55
55
  });
56
56
  const Server = z.object({
57
- url: z.url(),
57
+ url: z.string(), // Allow templated URLs with {variables}
58
58
  description: z.string().optional(),
59
59
  variables: z.record(z.string(), ServerVariable).optional(),
60
60
  });