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.
@@ -53,15 +53,15 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
53
53
  private buildAST(openapi: OpenApiSpecType): ts.Statement[] {
54
54
  const imports = this.importBuilder.buildImports();
55
55
  const schemas = this.buildSchemas(openapi);
56
+ const serverConfig = this.buildServerConfiguration(openapi);
56
57
  const clientClass = this.buildClientClass(openapi, schemas);
57
- const baseUrlConstant = this.buildBaseUrlConstant(openapi);
58
58
 
59
59
  return [
60
60
  this.createComment('Imports'),
61
61
  ...imports,
62
62
  this.createComment('Components schemas'),
63
63
  ...Object.values(schemas),
64
- ...baseUrlConstant,
64
+ ...serverConfig,
65
65
  this.createComment('Client class'),
66
66
  clientClass,
67
67
  ];
@@ -111,33 +111,132 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
111
111
  undefined,
112
112
  [
113
113
  this.typeBuilder.createProperty('#baseUrl', 'string', true),
114
- this.buildConstructor(),
114
+ this.buildConstructor(openapi),
115
+ this.buildGetBaseRequestOptionsMethod(),
115
116
  this.buildHttpRequestMethod(),
116
117
  ...methods,
117
118
  ],
118
119
  );
119
120
  }
120
121
 
121
- private buildConstructor(): ts.ConstructorDeclaration {
122
- return ts.factory.createConstructorDeclaration(
123
- undefined,
124
- [
125
- this.typeBuilder.createParameter('baseUrl', 'string', ts.factory.createIdentifier('defaultBaseUrl')),
126
- this.typeBuilder.createParameter('_', 'unknown', undefined, true),
127
- ],
128
- ts.factory.createBlock(
122
+ private buildConstructor(openapi: OpenApiSpecType): ts.ConstructorDeclaration {
123
+ const hasServers = openapi.servers && openapi.servers.length > 0;
124
+
125
+ if (hasServers) {
126
+ // Options-based constructor
127
+ return ts.factory.createConstructorDeclaration(
128
+ undefined,
129
129
  [
130
- ts.factory.createExpressionStatement(
131
- ts.factory.createBinaryExpression(
132
- ts.factory.createPropertyAccessExpression(
133
- ts.factory.createThis(),
134
- ts.factory.createPrivateIdentifier('#baseUrl'),
130
+ ts.factory.createParameterDeclaration(
131
+ undefined,
132
+ undefined,
133
+ ts.factory.createIdentifier('options'),
134
+ undefined,
135
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('ClientOptions'), undefined),
136
+ undefined,
137
+ ),
138
+ ],
139
+ ts.factory.createBlock(
140
+ [
141
+ ts.factory.createVariableStatement(
142
+ undefined,
143
+ ts.factory.createVariableDeclarationList(
144
+ [
145
+ ts.factory.createVariableDeclaration(
146
+ ts.factory.createIdentifier('resolvedUrl'),
147
+ undefined,
148
+ undefined,
149
+ ts.factory.createConditionalExpression(
150
+ ts.factory.createBinaryExpression(
151
+ ts.factory.createPropertyAccessExpression(
152
+ ts.factory.createIdentifier('options'),
153
+ ts.factory.createIdentifier('baseUrl'),
154
+ ),
155
+ ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken),
156
+ ts.factory.createNull(),
157
+ ),
158
+ ts.factory.createToken(ts.SyntaxKind.QuestionToken),
159
+ ts.factory.createPropertyAccessExpression(
160
+ ts.factory.createIdentifier('options'),
161
+ ts.factory.createIdentifier('baseUrl'),
162
+ ),
163
+ ts.factory.createToken(ts.SyntaxKind.ColonToken),
164
+ ts.factory.createCallExpression(ts.factory.createIdentifier('resolveServerUrl'), undefined, [
165
+ ts.factory.createPropertyAccessExpression(
166
+ ts.factory.createIdentifier('options'),
167
+ ts.factory.createIdentifier('serverIndex'),
168
+ ),
169
+ ts.factory.createPropertyAccessExpression(
170
+ ts.factory.createIdentifier('options'),
171
+ ts.factory.createIdentifier('serverVariables'),
172
+ ),
173
+ ]),
174
+ ),
175
+ ),
176
+ ],
177
+ ts.NodeFlags.Const,
135
178
  ),
136
- ts.factory.createToken(ts.SyntaxKind.EqualsToken),
137
- ts.factory.createIdentifier('baseUrl'),
138
179
  ),
139
- ),
180
+ ts.factory.createExpressionStatement(
181
+ ts.factory.createBinaryExpression(
182
+ ts.factory.createPropertyAccessExpression(
183
+ ts.factory.createThis(),
184
+ ts.factory.createPrivateIdentifier('#baseUrl'),
185
+ ),
186
+ ts.factory.createToken(ts.SyntaxKind.EqualsToken),
187
+ ts.factory.createIdentifier('resolvedUrl'),
188
+ ),
189
+ ),
190
+ ],
191
+ true,
192
+ ),
193
+ );
194
+ } else {
195
+ // Fallback: simple baseUrl parameter
196
+ return ts.factory.createConstructorDeclaration(
197
+ undefined,
198
+ [
199
+ this.typeBuilder.createParameter('baseUrl', 'string', ts.factory.createStringLiteral('/', true)),
200
+ this.typeBuilder.createParameter('_', 'unknown', undefined, true),
140
201
  ],
202
+ ts.factory.createBlock(
203
+ [
204
+ ts.factory.createExpressionStatement(
205
+ ts.factory.createBinaryExpression(
206
+ ts.factory.createPropertyAccessExpression(
207
+ ts.factory.createThis(),
208
+ ts.factory.createPrivateIdentifier('#baseUrl'),
209
+ ),
210
+ ts.factory.createToken(ts.SyntaxKind.EqualsToken),
211
+ ts.factory.createIdentifier('baseUrl'),
212
+ ),
213
+ ),
214
+ ],
215
+ true,
216
+ ),
217
+ );
218
+ }
219
+ }
220
+
221
+ private buildGetBaseRequestOptionsMethod(): ts.MethodDeclaration {
222
+ return ts.factory.createMethodDeclaration(
223
+ [ts.factory.createToken(ts.SyntaxKind.ProtectedKeyword)],
224
+ undefined,
225
+ ts.factory.createIdentifier('getBaseRequestOptions'),
226
+ undefined,
227
+ undefined,
228
+ [],
229
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Partial'), [
230
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Omit'), [
231
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('RequestInit'), undefined),
232
+ ts.factory.createUnionTypeNode([
233
+ ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral('method', true)),
234
+ ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral('body', true)),
235
+ ]),
236
+ ]),
237
+ ]),
238
+ ts.factory.createBlock(
239
+ [ts.factory.createReturnStatement(ts.factory.createObjectLiteralExpression([], false))],
141
240
  true,
142
241
  ),
143
242
  );
@@ -155,7 +254,7 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
155
254
  this.typeBuilder.createParameter('path', 'string'),
156
255
  this.typeBuilder.createParameter(
157
256
  'options',
158
- '{params?: Record<string, string | number | boolean>; data?: unknown; contentType?: string}',
257
+ '{params?: Record<string, string | number | boolean>; data?: unknown; contentType?: string; headers?: Record<string, string>}',
159
258
  ts.factory.createObjectLiteralExpression([], false),
160
259
  false,
161
260
  ),
@@ -355,7 +454,87 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
355
454
  ts.NodeFlags.Const,
356
455
  ),
357
456
  ),
358
- // Build headers with dynamic Content-Type
457
+ // Get base request options (headers, signal, credentials, etc.)
458
+ ts.factory.createVariableStatement(
459
+ undefined,
460
+ ts.factory.createVariableDeclarationList(
461
+ [
462
+ ts.factory.createVariableDeclaration(
463
+ ts.factory.createIdentifier('baseOptions'),
464
+ undefined,
465
+ undefined,
466
+ ts.factory.createCallExpression(
467
+ ts.factory.createPropertyAccessExpression(
468
+ ts.factory.createThis(),
469
+ ts.factory.createIdentifier('getBaseRequestOptions'),
470
+ ),
471
+ undefined,
472
+ [],
473
+ ),
474
+ ),
475
+ ],
476
+ ts.NodeFlags.Const,
477
+ ),
478
+ ),
479
+ // Build Content-Type header
480
+ ts.factory.createVariableStatement(
481
+ undefined,
482
+ ts.factory.createVariableDeclarationList(
483
+ [
484
+ ts.factory.createVariableDeclaration(
485
+ ts.factory.createIdentifier('contentType'),
486
+ undefined,
487
+ undefined,
488
+ ts.factory.createConditionalExpression(
489
+ ts.factory.createBinaryExpression(
490
+ ts.factory.createPropertyAccessExpression(
491
+ ts.factory.createIdentifier('options'),
492
+ ts.factory.createIdentifier('contentType'),
493
+ ),
494
+ ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
495
+ ts.factory.createStringLiteral('application/x-www-form-urlencoded', true),
496
+ ),
497
+ undefined,
498
+ ts.factory.createStringLiteral('application/x-www-form-urlencoded', true),
499
+ undefined,
500
+ ts.factory.createStringLiteral('application/json', true),
501
+ ),
502
+ ),
503
+ ],
504
+ ts.NodeFlags.Const,
505
+ ),
506
+ ),
507
+ // Merge headers: base headers, Content-Type, and request-specific headers
508
+ ts.factory.createVariableStatement(
509
+ undefined,
510
+ ts.factory.createVariableDeclarationList(
511
+ [
512
+ ts.factory.createVariableDeclaration(
513
+ ts.factory.createIdentifier('baseHeaders'),
514
+ undefined,
515
+ undefined,
516
+ ts.factory.createConditionalExpression(
517
+ ts.factory.createBinaryExpression(
518
+ ts.factory.createPropertyAccessExpression(
519
+ ts.factory.createIdentifier('baseOptions'),
520
+ ts.factory.createIdentifier('headers'),
521
+ ),
522
+ ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken),
523
+ ts.factory.createIdentifier('undefined'),
524
+ ),
525
+ undefined,
526
+ ts.factory.createPropertyAccessExpression(
527
+ ts.factory.createIdentifier('baseOptions'),
528
+ ts.factory.createIdentifier('headers'),
529
+ ),
530
+ undefined,
531
+ ts.factory.createObjectLiteralExpression([], false),
532
+ ),
533
+ ),
534
+ ],
535
+ ts.NodeFlags.Const,
536
+ ),
537
+ ),
359
538
  ts.factory.createVariableStatement(
360
539
  undefined,
361
540
  ts.factory.createVariableDeclarationList(
@@ -364,27 +543,42 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
364
543
  ts.factory.createIdentifier('headers'),
365
544
  undefined,
366
545
  undefined,
367
- ts.factory.createObjectLiteralExpression(
546
+ ts.factory.createCallExpression(
547
+ ts.factory.createPropertyAccessExpression(
548
+ ts.factory.createIdentifier('Object'),
549
+ ts.factory.createIdentifier('assign'),
550
+ ),
551
+ undefined,
368
552
  [
369
- ts.factory.createPropertyAssignment(
370
- ts.factory.createStringLiteral('Content-Type', true),
371
- ts.factory.createConditionalExpression(
372
- ts.factory.createBinaryExpression(
373
- ts.factory.createPropertyAccessExpression(
374
- ts.factory.createIdentifier('options'),
375
- ts.factory.createIdentifier('contentType'),
376
- ),
377
- ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
378
- ts.factory.createStringLiteral('application/x-www-form-urlencoded', true),
553
+ ts.factory.createObjectLiteralExpression([], false),
554
+ ts.factory.createIdentifier('baseHeaders'),
555
+ ts.factory.createObjectLiteralExpression(
556
+ [
557
+ ts.factory.createPropertyAssignment(
558
+ ts.factory.createStringLiteral('Content-Type', true),
559
+ ts.factory.createIdentifier('contentType'),
379
560
  ),
380
- undefined,
381
- ts.factory.createStringLiteral('application/x-www-form-urlencoded', true),
382
- undefined,
383
- ts.factory.createStringLiteral('application/json', true),
561
+ ],
562
+ false,
563
+ ),
564
+ ts.factory.createConditionalExpression(
565
+ ts.factory.createBinaryExpression(
566
+ ts.factory.createPropertyAccessExpression(
567
+ ts.factory.createIdentifier('options'),
568
+ ts.factory.createIdentifier('headers'),
569
+ ),
570
+ ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken),
571
+ ts.factory.createIdentifier('undefined'),
572
+ ),
573
+ undefined,
574
+ ts.factory.createPropertyAccessExpression(
575
+ ts.factory.createIdentifier('options'),
576
+ ts.factory.createIdentifier('headers'),
384
577
  ),
578
+ undefined,
579
+ ts.factory.createObjectLiteralExpression([], false),
385
580
  ),
386
581
  ],
387
- true,
388
582
  ),
389
583
  ),
390
584
  ],
@@ -564,7 +758,7 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
564
758
  ts.NodeFlags.Const,
565
759
  ),
566
760
  ),
567
- // Make fetch request
761
+ // Make fetch request: merge base options with method, headers, and body
568
762
  ts.factory.createVariableStatement(
569
763
  undefined,
570
764
  ts.factory.createVariableDeclarationList(
@@ -576,22 +770,33 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
576
770
  ts.factory.createAwaitExpression(
577
771
  ts.factory.createCallExpression(ts.factory.createIdentifier('fetch'), undefined, [
578
772
  ts.factory.createIdentifier('url'),
579
- ts.factory.createObjectLiteralExpression(
773
+ ts.factory.createCallExpression(
774
+ ts.factory.createPropertyAccessExpression(
775
+ ts.factory.createIdentifier('Object'),
776
+ ts.factory.createIdentifier('assign'),
777
+ ),
778
+ undefined,
580
779
  [
581
- ts.factory.createShorthandPropertyAssignment(
582
- ts.factory.createIdentifier('method'),
583
- undefined,
584
- ),
585
- ts.factory.createPropertyAssignment(
586
- ts.factory.createIdentifier('headers'),
587
- ts.factory.createIdentifier('headers'),
588
- ),
589
- ts.factory.createPropertyAssignment(
590
- ts.factory.createIdentifier('body'),
591
- ts.factory.createIdentifier('body'),
780
+ ts.factory.createObjectLiteralExpression([], false),
781
+ ts.factory.createIdentifier('baseOptions'),
782
+ ts.factory.createObjectLiteralExpression(
783
+ [
784
+ ts.factory.createShorthandPropertyAssignment(
785
+ ts.factory.createIdentifier('method'),
786
+ undefined,
787
+ ),
788
+ ts.factory.createPropertyAssignment(
789
+ ts.factory.createIdentifier('headers'),
790
+ ts.factory.createIdentifier('headers'),
791
+ ),
792
+ ts.factory.createPropertyAssignment(
793
+ ts.factory.createIdentifier('body'),
794
+ ts.factory.createIdentifier('body'),
795
+ ),
796
+ ],
797
+ false,
592
798
  ),
593
799
  ],
594
- true,
595
800
  ),
596
801
  ]),
597
802
  ),
@@ -1049,28 +1254,407 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
1049
1254
  ]);
1050
1255
  }
1051
1256
 
1052
- private buildBaseUrlConstant(openapi: OpenApiSpecType): ts.Statement[] {
1053
- const baseUrl = openapi.servers?.[0]?.url;
1054
- if (!baseUrl) {
1257
+ private buildServerConfiguration(openapi: OpenApiSpecType): ts.Statement[] {
1258
+ const servers = openapi.servers;
1259
+
1260
+ if (!servers || servers.length === 0) {
1055
1261
  return [];
1056
1262
  }
1057
1263
 
1058
- return [
1264
+ const statements: ts.Statement[] = [];
1265
+
1266
+ // Build server configuration array
1267
+ const serverConfigElements = servers.map((server) => {
1268
+ const properties: ts.PropertyAssignment[] = [
1269
+ ts.factory.createPropertyAssignment('url', ts.factory.createStringLiteral(server.url, true)),
1270
+ ];
1271
+
1272
+ if (server.description) {
1273
+ properties.push(
1274
+ ts.factory.createPropertyAssignment('description', ts.factory.createStringLiteral(server.description, true)),
1275
+ );
1276
+ }
1277
+
1278
+ if (server.variables && Object.keys(server.variables).length > 0) {
1279
+ const variableProperties = Object.entries(server.variables).map(([varName, varDef]) => {
1280
+ const varProps: ts.PropertyAssignment[] = [
1281
+ ts.factory.createPropertyAssignment('default', ts.factory.createStringLiteral(varDef.default, true)),
1282
+ ];
1283
+
1284
+ if (varDef.enum && varDef.enum.length > 0) {
1285
+ varProps.push(
1286
+ ts.factory.createPropertyAssignment(
1287
+ 'enum',
1288
+ ts.factory.createArrayLiteralExpression(
1289
+ varDef.enum.map((val) => ts.factory.createStringLiteral(val, true)),
1290
+ false,
1291
+ ),
1292
+ ),
1293
+ );
1294
+ }
1295
+
1296
+ if (varDef.description) {
1297
+ varProps.push(
1298
+ ts.factory.createPropertyAssignment(
1299
+ 'description',
1300
+ ts.factory.createStringLiteral(varDef.description, true),
1301
+ ),
1302
+ );
1303
+ }
1304
+
1305
+ return ts.factory.createPropertyAssignment(varName, ts.factory.createObjectLiteralExpression(varProps, true));
1306
+ });
1307
+
1308
+ properties.push(
1309
+ ts.factory.createPropertyAssignment(
1310
+ 'variables',
1311
+ ts.factory.createObjectLiteralExpression(variableProperties, true),
1312
+ ),
1313
+ );
1314
+ }
1315
+
1316
+ return ts.factory.createObjectLiteralExpression(properties, true);
1317
+ });
1318
+
1319
+ // Export server configuration
1320
+ statements.push(
1059
1321
  ts.factory.createVariableStatement(
1060
- undefined,
1322
+ [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
1323
+ ts.factory.createVariableDeclarationList(
1324
+ [
1325
+ ts.factory.createVariableDeclaration(
1326
+ ts.factory.createIdentifier('serverConfigurations'),
1327
+ undefined,
1328
+ undefined,
1329
+ ts.factory.createArrayLiteralExpression(serverConfigElements, false),
1330
+ ),
1331
+ ],
1332
+ ts.NodeFlags.Const,
1333
+ ),
1334
+ ),
1335
+ );
1336
+
1337
+ // Export default base URL (first server with default variables)
1338
+ const firstServer = servers[0];
1339
+ const defaultBaseUrl = firstServer ? this.resolveServerUrl(firstServer, {}) : '/';
1340
+ statements.push(
1341
+ ts.factory.createVariableStatement(
1342
+ [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
1061
1343
  ts.factory.createVariableDeclarationList(
1062
1344
  [
1063
1345
  ts.factory.createVariableDeclaration(
1064
1346
  ts.factory.createIdentifier('defaultBaseUrl'),
1065
1347
  undefined,
1066
1348
  undefined,
1067
- ts.factory.createStringLiteral(baseUrl, true),
1349
+ ts.factory.createStringLiteral(defaultBaseUrl, true),
1068
1350
  ),
1069
1351
  ],
1070
1352
  ts.NodeFlags.Const,
1071
1353
  ),
1072
1354
  ),
1355
+ );
1356
+
1357
+ // Build ClientOptions type
1358
+ const optionProperties: ts.PropertySignature[] = [
1359
+ ts.factory.createPropertySignature(
1360
+ undefined,
1361
+ ts.factory.createIdentifier('baseUrl'),
1362
+ ts.factory.createToken(ts.SyntaxKind.QuestionToken),
1363
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
1364
+ ),
1365
+ ts.factory.createPropertySignature(
1366
+ undefined,
1367
+ ts.factory.createIdentifier('serverIndex'),
1368
+ ts.factory.createToken(ts.SyntaxKind.QuestionToken),
1369
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
1370
+ ),
1371
+ ts.factory.createPropertySignature(
1372
+ undefined,
1373
+ ts.factory.createIdentifier('serverVariables'),
1374
+ ts.factory.createToken(ts.SyntaxKind.QuestionToken),
1375
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Record'), [
1376
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
1377
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
1378
+ ]),
1379
+ ),
1073
1380
  ];
1381
+
1382
+ statements.push(
1383
+ ts.factory.createTypeAliasDeclaration(
1384
+ [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
1385
+ ts.factory.createIdentifier('ClientOptions'),
1386
+ undefined,
1387
+ ts.factory.createTypeLiteralNode(optionProperties),
1388
+ ),
1389
+ );
1390
+
1391
+ // Build resolveServerUrl helper function
1392
+ statements.push(this.buildResolveServerUrlFunction(servers));
1393
+
1394
+ return statements;
1395
+ }
1396
+
1397
+ private resolveServerUrl(
1398
+ server: {
1399
+ url: string;
1400
+ variables?:
1401
+ | Record<string, {default: string; enum?: string[] | undefined; description?: string | undefined}>
1402
+ | undefined;
1403
+ },
1404
+ variables: Record<string, string>,
1405
+ ): string {
1406
+ let url = server.url;
1407
+
1408
+ if (server.variables) {
1409
+ for (const [varName, varDef] of Object.entries(server.variables)) {
1410
+ const value = variables[varName] ?? varDef.default;
1411
+ url = url.replace(`{${varName}}`, value);
1412
+ }
1413
+ }
1414
+
1415
+ return url;
1416
+ }
1417
+
1418
+ private buildResolveServerUrlFunction(
1419
+ servers: {
1420
+ url: string;
1421
+ description?: string | undefined;
1422
+ variables?:
1423
+ | Record<string, {default: string; enum?: string[] | undefined; description?: string | undefined}>
1424
+ | undefined;
1425
+ }[],
1426
+ ): ts.FunctionDeclaration {
1427
+ // Build server configs array inline
1428
+ const serverConfigElements = servers.map((server) => {
1429
+ const properties: ts.PropertyAssignment[] = [
1430
+ ts.factory.createPropertyAssignment('url', ts.factory.createStringLiteral(server.url, true)),
1431
+ ];
1432
+
1433
+ if (server.variables && Object.keys(server.variables).length > 0) {
1434
+ const variableProperties = Object.entries(server.variables).map(([varName, varDef]) => {
1435
+ const varProps: ts.PropertyAssignment[] = [
1436
+ ts.factory.createPropertyAssignment('default', ts.factory.createStringLiteral(varDef.default, true)),
1437
+ ];
1438
+
1439
+ if (varDef.enum && varDef.enum.length > 0) {
1440
+ varProps.push(
1441
+ ts.factory.createPropertyAssignment(
1442
+ 'enum',
1443
+ ts.factory.createArrayLiteralExpression(
1444
+ varDef.enum.map((val) => ts.factory.createStringLiteral(val, true)),
1445
+ false,
1446
+ ),
1447
+ ),
1448
+ );
1449
+ }
1450
+
1451
+ return ts.factory.createPropertyAssignment(varName, ts.factory.createObjectLiteralExpression(varProps, true));
1452
+ });
1453
+
1454
+ properties.push(
1455
+ ts.factory.createPropertyAssignment(
1456
+ 'variables',
1457
+ ts.factory.createObjectLiteralExpression(variableProperties, true),
1458
+ ),
1459
+ );
1460
+ }
1461
+
1462
+ return ts.factory.createObjectLiteralExpression(properties, true);
1463
+ });
1464
+
1465
+ // Build function body - simplified version
1466
+ const idx = ts.factory.createIdentifier('idx');
1467
+ const configs = ts.factory.createIdentifier('configs');
1468
+ const config = ts.factory.createIdentifier('config');
1469
+ const url = ts.factory.createIdentifier('url');
1470
+ const key = ts.factory.createIdentifier('key');
1471
+ const value = ts.factory.createIdentifier('value');
1472
+
1473
+ const bodyStatements: ts.Statement[] = [
1474
+ // const configs = [...]
1475
+ ts.factory.createVariableStatement(
1476
+ undefined,
1477
+ ts.factory.createVariableDeclarationList(
1478
+ [
1479
+ ts.factory.createVariableDeclaration(
1480
+ configs,
1481
+ undefined,
1482
+ undefined,
1483
+ ts.factory.createArrayLiteralExpression(serverConfigElements, false),
1484
+ ),
1485
+ ],
1486
+ ts.NodeFlags.Const,
1487
+ ),
1488
+ ),
1489
+ // const idx = serverIndex ?? 0
1490
+ ts.factory.createVariableStatement(
1491
+ undefined,
1492
+ ts.factory.createVariableDeclarationList(
1493
+ [
1494
+ ts.factory.createVariableDeclaration(
1495
+ idx,
1496
+ undefined,
1497
+ undefined,
1498
+ ts.factory.createBinaryExpression(
1499
+ ts.factory.createIdentifier('serverIndex'),
1500
+ ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
1501
+ ts.factory.createNumericLiteral('0'),
1502
+ ),
1503
+ ),
1504
+ ],
1505
+ ts.NodeFlags.Const,
1506
+ ),
1507
+ ),
1508
+ // if (idx < configs.length) { ... }
1509
+ ts.factory.createIfStatement(
1510
+ ts.factory.createBinaryExpression(
1511
+ idx,
1512
+ ts.factory.createToken(ts.SyntaxKind.LessThanToken),
1513
+ ts.factory.createPropertyAccessExpression(configs, ts.factory.createIdentifier('length')),
1514
+ ),
1515
+ ts.factory.createBlock(
1516
+ [
1517
+ // const config = configs[idx]
1518
+ ts.factory.createVariableStatement(
1519
+ undefined,
1520
+ ts.factory.createVariableDeclarationList(
1521
+ [
1522
+ ts.factory.createVariableDeclaration(
1523
+ config,
1524
+ undefined,
1525
+ undefined,
1526
+ ts.factory.createElementAccessExpression(configs, idx),
1527
+ ),
1528
+ ],
1529
+ ts.NodeFlags.Const,
1530
+ ),
1531
+ ),
1532
+ // let url = config.url
1533
+ ts.factory.createVariableStatement(
1534
+ undefined,
1535
+ ts.factory.createVariableDeclarationList(
1536
+ [
1537
+ ts.factory.createVariableDeclaration(
1538
+ url,
1539
+ undefined,
1540
+ undefined,
1541
+ ts.factory.createPropertyAccessExpression(config, ts.factory.createIdentifier('url')),
1542
+ ),
1543
+ ],
1544
+ ts.NodeFlags.Let,
1545
+ ),
1546
+ ),
1547
+ // if (config.variables && serverVariables) { ... }
1548
+ ts.factory.createIfStatement(
1549
+ ts.factory.createLogicalAnd(
1550
+ ts.factory.createPropertyAccessExpression(config, ts.factory.createIdentifier('variables')),
1551
+ ts.factory.createIdentifier('serverVariables'),
1552
+ ),
1553
+ ts.factory.createBlock(
1554
+ [
1555
+ // for (const [key, value] of Object.entries(serverVariables)) { url = url.replace(...) }
1556
+ ts.factory.createForOfStatement(
1557
+ undefined,
1558
+ ts.factory.createVariableDeclarationList(
1559
+ [
1560
+ ts.factory.createVariableDeclaration(
1561
+ ts.factory.createArrayBindingPattern([
1562
+ ts.factory.createBindingElement(undefined, undefined, key),
1563
+ ts.factory.createBindingElement(undefined, undefined, value),
1564
+ ]),
1565
+ undefined,
1566
+ undefined,
1567
+ ),
1568
+ ],
1569
+ ts.NodeFlags.Const,
1570
+ ),
1571
+ ts.factory.createCallExpression(
1572
+ ts.factory.createPropertyAccessExpression(
1573
+ ts.factory.createIdentifier('Object'),
1574
+ ts.factory.createIdentifier('entries'),
1575
+ ),
1576
+ undefined,
1577
+ [ts.factory.createIdentifier('serverVariables')],
1578
+ ),
1579
+ ts.factory.createBlock(
1580
+ [
1581
+ ts.factory.createExpressionStatement(
1582
+ ts.factory.createBinaryExpression(
1583
+ url,
1584
+ ts.factory.createToken(ts.SyntaxKind.EqualsToken),
1585
+ ts.factory.createCallExpression(
1586
+ ts.factory.createPropertyAccessExpression(url, ts.factory.createIdentifier('replace')),
1587
+ undefined,
1588
+ [
1589
+ ts.factory.createNewExpression(ts.factory.createIdentifier('RegExp'), undefined, [
1590
+ ts.factory.createBinaryExpression(
1591
+ ts.factory.createBinaryExpression(
1592
+ ts.factory.createStringLiteral('\\{'),
1593
+ ts.factory.createToken(ts.SyntaxKind.PlusToken),
1594
+ key,
1595
+ ),
1596
+ ts.factory.createToken(ts.SyntaxKind.PlusToken),
1597
+ ts.factory.createStringLiteral('\\}'),
1598
+ ),
1599
+ ts.factory.createStringLiteral('g'),
1600
+ ]),
1601
+ value,
1602
+ ],
1603
+ ),
1604
+ ),
1605
+ ),
1606
+ ],
1607
+ true,
1608
+ ),
1609
+ ),
1610
+ ],
1611
+ true,
1612
+ ),
1613
+ ),
1614
+ // return url
1615
+ ts.factory.createReturnStatement(url),
1616
+ ],
1617
+ true,
1618
+ ),
1619
+ ),
1620
+ // return default (first server with defaults)
1621
+ ts.factory.createReturnStatement(
1622
+ ts.factory.createStringLiteral(servers[0] ? this.resolveServerUrl(servers[0], {}) : '/', true),
1623
+ ),
1624
+ ];
1625
+
1626
+ return ts.factory.createFunctionDeclaration(
1627
+ undefined,
1628
+ undefined,
1629
+ ts.factory.createIdentifier('resolveServerUrl'),
1630
+ undefined,
1631
+ [
1632
+ ts.factory.createParameterDeclaration(
1633
+ undefined,
1634
+ undefined,
1635
+ ts.factory.createIdentifier('serverIndex'),
1636
+ ts.factory.createToken(ts.SyntaxKind.QuestionToken),
1637
+ ts.factory.createUnionTypeNode([
1638
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword),
1639
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword),
1640
+ ]),
1641
+ undefined,
1642
+ ),
1643
+ ts.factory.createParameterDeclaration(
1644
+ undefined,
1645
+ undefined,
1646
+ ts.factory.createIdentifier('serverVariables'),
1647
+ ts.factory.createToken(ts.SyntaxKind.QuestionToken),
1648
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Record'), [
1649
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
1650
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
1651
+ ]),
1652
+ ts.factory.createObjectLiteralExpression([], false),
1653
+ ),
1654
+ ],
1655
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
1656
+ ts.factory.createBlock(bodyStatements, true),
1657
+ );
1074
1658
  }
1075
1659
 
1076
1660
  private generateClientName(title: string): string {
@@ -1166,42 +1750,77 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
1166
1750
 
1167
1751
  // Handle enum
1168
1752
  if (prop['enum'] && Array.isArray(prop['enum']) && prop['enum'].length > 0) {
1169
- const enumValues = prop['enum'].map((val) => {
1170
- if (typeof val === 'string') {
1171
- return ts.factory.createStringLiteral(val, true);
1172
- }
1173
- if (typeof val === 'number') {
1174
- // Handle negative numbers correctly
1175
- if (val < 0) {
1176
- return ts.factory.createPrefixUnaryExpression(
1177
- ts.SyntaxKind.MinusToken,
1178
- ts.factory.createNumericLiteral(String(Math.abs(val))),
1753
+ // Check if all enum values are strings (z.enum only works with strings)
1754
+ const allStrings = prop['enum'].every((val) => typeof val === 'string');
1755
+
1756
+ if (allStrings) {
1757
+ // Use z.enum() for string enums
1758
+ const enumValues = prop['enum'].map((val) => ts.factory.createStringLiteral(val as string, true));
1759
+ const enumExpression = ts.factory.createCallExpression(
1760
+ ts.factory.createPropertyAccessExpression(
1761
+ ts.factory.createIdentifier('z'),
1762
+ ts.factory.createIdentifier('enum'),
1763
+ ),
1764
+ undefined,
1765
+ [ts.factory.createArrayLiteralExpression(enumValues, false)],
1766
+ );
1767
+
1768
+ return required
1769
+ ? enumExpression
1770
+ : ts.factory.createCallExpression(
1771
+ ts.factory.createPropertyAccessExpression(enumExpression, ts.factory.createIdentifier('optional')),
1772
+ undefined,
1773
+ [],
1179
1774
  );
1775
+ } else {
1776
+ // Use z.union([z.literal(...), ...]) for numeric/boolean/mixed enums
1777
+ const literalSchemas = prop['enum'].map((val) => {
1778
+ let literalValue: ts.Expression;
1779
+ if (typeof val === 'string') {
1780
+ literalValue = ts.factory.createStringLiteral(val, true);
1781
+ } else if (typeof val === 'number') {
1782
+ // Handle negative numbers correctly
1783
+ if (val < 0) {
1784
+ literalValue = ts.factory.createPrefixUnaryExpression(
1785
+ ts.SyntaxKind.MinusToken,
1786
+ ts.factory.createNumericLiteral(String(Math.abs(val))),
1787
+ );
1788
+ } else {
1789
+ literalValue = ts.factory.createNumericLiteral(String(val));
1790
+ }
1791
+ } else if (typeof val === 'boolean') {
1792
+ literalValue = val ? ts.factory.createTrue() : ts.factory.createFalse();
1793
+ } else {
1794
+ literalValue = ts.factory.createStringLiteral(String(val), true);
1180
1795
  }
1181
- return ts.factory.createNumericLiteral(String(val));
1182
- }
1183
- if (typeof val === 'boolean') {
1184
- return val ? ts.factory.createTrue() : ts.factory.createFalse();
1185
- }
1186
- return ts.factory.createStringLiteral(String(val), true);
1187
- });
1188
-
1189
- const enumExpression = ts.factory.createCallExpression(
1190
- ts.factory.createPropertyAccessExpression(
1191
- ts.factory.createIdentifier('z'),
1192
- ts.factory.createIdentifier('enum'),
1193
- ),
1194
- undefined,
1195
- [ts.factory.createArrayLiteralExpression(enumValues, false)],
1196
- );
1197
1796
 
1198
- return required
1199
- ? enumExpression
1200
- : ts.factory.createCallExpression(
1201
- ts.factory.createPropertyAccessExpression(enumExpression, ts.factory.createIdentifier('optional')),
1797
+ return ts.factory.createCallExpression(
1798
+ ts.factory.createPropertyAccessExpression(
1799
+ ts.factory.createIdentifier('z'),
1800
+ ts.factory.createIdentifier('literal'),
1801
+ ),
1202
1802
  undefined,
1203
- [],
1803
+ [literalValue],
1204
1804
  );
1805
+ });
1806
+
1807
+ const unionExpression = ts.factory.createCallExpression(
1808
+ ts.factory.createPropertyAccessExpression(
1809
+ ts.factory.createIdentifier('z'),
1810
+ ts.factory.createIdentifier('union'),
1811
+ ),
1812
+ undefined,
1813
+ [ts.factory.createArrayLiteralExpression(literalSchemas, false)],
1814
+ );
1815
+
1816
+ return required
1817
+ ? unionExpression
1818
+ : ts.factory.createCallExpression(
1819
+ ts.factory.createPropertyAccessExpression(unionExpression, ts.factory.createIdentifier('optional')),
1820
+ undefined,
1821
+ [],
1822
+ );
1823
+ }
1205
1824
  }
1206
1825
 
1207
1826
  switch (prop['type']) {