zod-codegen 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +5 -5
- package/.github/workflows/release.yml +1 -1
- package/.nvmrc +1 -1
- package/CHANGELOG.md +7 -0
- package/dist/src/assets/manifest.json +2 -2
- package/dist/src/generator.js +3 -3
- package/dist/src/http/fetch-client.js +0 -1
- package/dist/src/services/code-generator.service.js +549 -58
- package/dist/src/services/file-reader.service.js +27 -14
- package/dist/src/services/import-builder.service.js +4 -11
- package/dist/src/services/type-builder.service.js +16 -9
- package/dist/tests/unit/generator.test.js +1 -0
- package/eslint.config.mjs +18 -0
- package/package.json +22 -22
- package/src/assets/manifest.json +2 -2
- package/src/generator.ts +3 -3
- package/src/http/fetch-client.ts +4 -4
- package/src/services/code-generator.service.ts +1212 -88
- package/src/services/file-reader.service.ts +26 -13
- package/src/services/import-builder.service.ts +8 -11
- package/src/services/type-builder.service.ts +19 -12
- package/tsconfig.json +4 -4
- package/scripts/update-manifest.js +0 -31
|
@@ -28,20 +28,20 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
|
|
|
28
28
|
if (safeCategorySchema.success) {
|
|
29
29
|
const safeCategory = safeCategorySchema.data;
|
|
30
30
|
|
|
31
|
-
if (safeCategory
|
|
32
|
-
return this.handleLogicalOperator('anyOf', safeCategory
|
|
31
|
+
if (safeCategory['anyOf'] && Array.isArray(safeCategory['anyOf']) && safeCategory['anyOf'].length > 0) {
|
|
32
|
+
return this.handleLogicalOperator('anyOf', safeCategory['anyOf'], required);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
if (safeCategory
|
|
36
|
-
return this.handleLogicalOperator('oneOf', safeCategory
|
|
35
|
+
if (safeCategory['oneOf'] && Array.isArray(safeCategory['oneOf']) && safeCategory['oneOf'].length > 0) {
|
|
36
|
+
return this.handleLogicalOperator('oneOf', safeCategory['oneOf'], required);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
if (safeCategory
|
|
40
|
-
return this.handleLogicalOperator('allOf', safeCategory
|
|
39
|
+
if (safeCategory['allOf'] && Array.isArray(safeCategory['allOf']) && safeCategory['allOf'].length > 0) {
|
|
40
|
+
return this.handleLogicalOperator('allOf', safeCategory['allOf'], required);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
if (safeCategory
|
|
44
|
-
return this.handleLogicalOperator('not', [safeCategory
|
|
43
|
+
if (safeCategory['not']) {
|
|
44
|
+
return this.handleLogicalOperator('not', [safeCategory['not']], required);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
return this.buildProperty(safeCategory, required);
|
|
@@ -155,9 +155,9 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
|
|
|
155
155
|
this.typeBuilder.createParameter('path', 'string'),
|
|
156
156
|
this.typeBuilder.createParameter(
|
|
157
157
|
'options',
|
|
158
|
-
'unknown',
|
|
158
|
+
'{params?: Record<string, string | number | boolean>; data?: unknown; contentType?: string}',
|
|
159
159
|
ts.factory.createObjectLiteralExpression([], false),
|
|
160
|
-
|
|
160
|
+
false,
|
|
161
161
|
),
|
|
162
162
|
],
|
|
163
163
|
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
|
|
@@ -165,12 +165,13 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
|
|
|
165
165
|
]),
|
|
166
166
|
ts.factory.createBlock(
|
|
167
167
|
[
|
|
168
|
+
// Build URL with query parameters
|
|
168
169
|
ts.factory.createVariableStatement(
|
|
169
170
|
undefined,
|
|
170
171
|
ts.factory.createVariableDeclarationList(
|
|
171
172
|
[
|
|
172
173
|
ts.factory.createVariableDeclaration(
|
|
173
|
-
ts.factory.createIdentifier('
|
|
174
|
+
ts.factory.createIdentifier('baseUrl'),
|
|
174
175
|
undefined,
|
|
175
176
|
undefined,
|
|
176
177
|
ts.factory.createTemplateExpression(ts.factory.createTemplateHead('', ''), [
|
|
@@ -191,6 +192,379 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
|
|
|
191
192
|
ts.NodeFlags.Const,
|
|
192
193
|
),
|
|
193
194
|
),
|
|
195
|
+
ts.factory.createVariableStatement(
|
|
196
|
+
undefined,
|
|
197
|
+
ts.factory.createVariableDeclarationList(
|
|
198
|
+
[
|
|
199
|
+
ts.factory.createVariableDeclaration(
|
|
200
|
+
ts.factory.createIdentifier('url'),
|
|
201
|
+
undefined,
|
|
202
|
+
undefined,
|
|
203
|
+
ts.factory.createConditionalExpression(
|
|
204
|
+
ts.factory.createBinaryExpression(
|
|
205
|
+
ts.factory.createPropertyAccessExpression(
|
|
206
|
+
ts.factory.createIdentifier('options'),
|
|
207
|
+
ts.factory.createIdentifier('params'),
|
|
208
|
+
),
|
|
209
|
+
ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken),
|
|
210
|
+
ts.factory.createBinaryExpression(
|
|
211
|
+
ts.factory.createPropertyAccessExpression(
|
|
212
|
+
ts.factory.createCallExpression(
|
|
213
|
+
ts.factory.createPropertyAccessExpression(
|
|
214
|
+
ts.factory.createIdentifier('Object'),
|
|
215
|
+
ts.factory.createIdentifier('keys'),
|
|
216
|
+
),
|
|
217
|
+
undefined,
|
|
218
|
+
[
|
|
219
|
+
ts.factory.createPropertyAccessExpression(
|
|
220
|
+
ts.factory.createIdentifier('options'),
|
|
221
|
+
ts.factory.createIdentifier('params'),
|
|
222
|
+
),
|
|
223
|
+
],
|
|
224
|
+
),
|
|
225
|
+
ts.factory.createIdentifier('length'),
|
|
226
|
+
),
|
|
227
|
+
ts.factory.createToken(ts.SyntaxKind.GreaterThanToken),
|
|
228
|
+
ts.factory.createNumericLiteral('0'),
|
|
229
|
+
),
|
|
230
|
+
),
|
|
231
|
+
undefined,
|
|
232
|
+
(() => {
|
|
233
|
+
const urlObj = ts.factory.createNewExpression(ts.factory.createIdentifier('URL'), undefined, [
|
|
234
|
+
ts.factory.createIdentifier('baseUrl'),
|
|
235
|
+
]);
|
|
236
|
+
const forEachCall = ts.factory.createCallExpression(
|
|
237
|
+
ts.factory.createPropertyAccessExpression(
|
|
238
|
+
ts.factory.createCallExpression(
|
|
239
|
+
ts.factory.createPropertyAccessExpression(
|
|
240
|
+
ts.factory.createIdentifier('Object'),
|
|
241
|
+
ts.factory.createIdentifier('entries'),
|
|
242
|
+
),
|
|
243
|
+
undefined,
|
|
244
|
+
[
|
|
245
|
+
ts.factory.createPropertyAccessExpression(
|
|
246
|
+
ts.factory.createIdentifier('options'),
|
|
247
|
+
ts.factory.createIdentifier('params'),
|
|
248
|
+
),
|
|
249
|
+
],
|
|
250
|
+
),
|
|
251
|
+
ts.factory.createIdentifier('forEach'),
|
|
252
|
+
),
|
|
253
|
+
undefined,
|
|
254
|
+
[
|
|
255
|
+
ts.factory.createArrowFunction(
|
|
256
|
+
undefined,
|
|
257
|
+
undefined,
|
|
258
|
+
[
|
|
259
|
+
ts.factory.createParameterDeclaration(
|
|
260
|
+
undefined,
|
|
261
|
+
undefined,
|
|
262
|
+
ts.factory.createArrayBindingPattern([
|
|
263
|
+
ts.factory.createBindingElement(
|
|
264
|
+
undefined,
|
|
265
|
+
undefined,
|
|
266
|
+
ts.factory.createIdentifier('key'),
|
|
267
|
+
undefined,
|
|
268
|
+
),
|
|
269
|
+
ts.factory.createBindingElement(
|
|
270
|
+
undefined,
|
|
271
|
+
undefined,
|
|
272
|
+
ts.factory.createIdentifier('value'),
|
|
273
|
+
undefined,
|
|
274
|
+
),
|
|
275
|
+
]),
|
|
276
|
+
undefined,
|
|
277
|
+
undefined,
|
|
278
|
+
),
|
|
279
|
+
],
|
|
280
|
+
undefined,
|
|
281
|
+
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
|
282
|
+
ts.factory.createBlock(
|
|
283
|
+
[
|
|
284
|
+
ts.factory.createExpressionStatement(
|
|
285
|
+
ts.factory.createCallExpression(
|
|
286
|
+
ts.factory.createPropertyAccessExpression(
|
|
287
|
+
ts.factory.createPropertyAccessExpression(
|
|
288
|
+
urlObj,
|
|
289
|
+
ts.factory.createIdentifier('searchParams'),
|
|
290
|
+
),
|
|
291
|
+
ts.factory.createIdentifier('set'),
|
|
292
|
+
),
|
|
293
|
+
undefined,
|
|
294
|
+
[
|
|
295
|
+
ts.factory.createIdentifier('key'),
|
|
296
|
+
ts.factory.createCallExpression(
|
|
297
|
+
ts.factory.createIdentifier('String'),
|
|
298
|
+
undefined,
|
|
299
|
+
[ts.factory.createIdentifier('value')],
|
|
300
|
+
),
|
|
301
|
+
],
|
|
302
|
+
),
|
|
303
|
+
),
|
|
304
|
+
],
|
|
305
|
+
false,
|
|
306
|
+
),
|
|
307
|
+
),
|
|
308
|
+
],
|
|
309
|
+
);
|
|
310
|
+
// Use IIFE to execute forEach and return URL string
|
|
311
|
+
return ts.factory.createCallExpression(
|
|
312
|
+
ts.factory.createParenthesizedExpression(
|
|
313
|
+
ts.factory.createArrowFunction(
|
|
314
|
+
undefined,
|
|
315
|
+
undefined,
|
|
316
|
+
[],
|
|
317
|
+
undefined,
|
|
318
|
+
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
|
319
|
+
ts.factory.createBlock(
|
|
320
|
+
[
|
|
321
|
+
ts.factory.createExpressionStatement(forEachCall),
|
|
322
|
+
ts.factory.createReturnStatement(
|
|
323
|
+
ts.factory.createCallExpression(
|
|
324
|
+
ts.factory.createPropertyAccessExpression(
|
|
325
|
+
urlObj,
|
|
326
|
+
ts.factory.createIdentifier('toString'),
|
|
327
|
+
),
|
|
328
|
+
undefined,
|
|
329
|
+
[],
|
|
330
|
+
),
|
|
331
|
+
),
|
|
332
|
+
],
|
|
333
|
+
false,
|
|
334
|
+
),
|
|
335
|
+
),
|
|
336
|
+
),
|
|
337
|
+
undefined,
|
|
338
|
+
[],
|
|
339
|
+
);
|
|
340
|
+
})(),
|
|
341
|
+
undefined,
|
|
342
|
+
ts.factory.createCallExpression(
|
|
343
|
+
ts.factory.createPropertyAccessExpression(
|
|
344
|
+
ts.factory.createNewExpression(ts.factory.createIdentifier('URL'), undefined, [
|
|
345
|
+
ts.factory.createIdentifier('baseUrl'),
|
|
346
|
+
]),
|
|
347
|
+
ts.factory.createIdentifier('toString'),
|
|
348
|
+
),
|
|
349
|
+
undefined,
|
|
350
|
+
[],
|
|
351
|
+
),
|
|
352
|
+
),
|
|
353
|
+
),
|
|
354
|
+
],
|
|
355
|
+
ts.NodeFlags.Const,
|
|
356
|
+
),
|
|
357
|
+
),
|
|
358
|
+
// Build headers with dynamic Content-Type
|
|
359
|
+
ts.factory.createVariableStatement(
|
|
360
|
+
undefined,
|
|
361
|
+
ts.factory.createVariableDeclarationList(
|
|
362
|
+
[
|
|
363
|
+
ts.factory.createVariableDeclaration(
|
|
364
|
+
ts.factory.createIdentifier('headers'),
|
|
365
|
+
undefined,
|
|
366
|
+
undefined,
|
|
367
|
+
ts.factory.createObjectLiteralExpression(
|
|
368
|
+
[
|
|
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),
|
|
379
|
+
),
|
|
380
|
+
undefined,
|
|
381
|
+
ts.factory.createStringLiteral('application/x-www-form-urlencoded', true),
|
|
382
|
+
undefined,
|
|
383
|
+
ts.factory.createStringLiteral('application/json', true),
|
|
384
|
+
),
|
|
385
|
+
),
|
|
386
|
+
],
|
|
387
|
+
true,
|
|
388
|
+
),
|
|
389
|
+
),
|
|
390
|
+
],
|
|
391
|
+
ts.NodeFlags.Const,
|
|
392
|
+
),
|
|
393
|
+
),
|
|
394
|
+
// Build body with form-urlencoded support
|
|
395
|
+
ts.factory.createVariableStatement(
|
|
396
|
+
undefined,
|
|
397
|
+
ts.factory.createVariableDeclarationList(
|
|
398
|
+
[
|
|
399
|
+
ts.factory.createVariableDeclaration(
|
|
400
|
+
ts.factory.createIdentifier('body'),
|
|
401
|
+
undefined,
|
|
402
|
+
undefined,
|
|
403
|
+
ts.factory.createConditionalExpression(
|
|
404
|
+
ts.factory.createBinaryExpression(
|
|
405
|
+
ts.factory.createPropertyAccessExpression(
|
|
406
|
+
ts.factory.createIdentifier('options'),
|
|
407
|
+
ts.factory.createIdentifier('data'),
|
|
408
|
+
),
|
|
409
|
+
ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken),
|
|
410
|
+
ts.factory.createIdentifier('undefined'),
|
|
411
|
+
),
|
|
412
|
+
undefined,
|
|
413
|
+
ts.factory.createConditionalExpression(
|
|
414
|
+
ts.factory.createBinaryExpression(
|
|
415
|
+
ts.factory.createPropertyAccessExpression(
|
|
416
|
+
ts.factory.createIdentifier('options'),
|
|
417
|
+
ts.factory.createIdentifier('contentType'),
|
|
418
|
+
),
|
|
419
|
+
ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
|
|
420
|
+
ts.factory.createStringLiteral('application/x-www-form-urlencoded', true),
|
|
421
|
+
),
|
|
422
|
+
undefined,
|
|
423
|
+
// Form-urlencoded: convert object to URLSearchParams
|
|
424
|
+
ts.factory.createCallExpression(
|
|
425
|
+
ts.factory.createParenthesizedExpression(
|
|
426
|
+
ts.factory.createArrowFunction(
|
|
427
|
+
undefined,
|
|
428
|
+
undefined,
|
|
429
|
+
[],
|
|
430
|
+
undefined,
|
|
431
|
+
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
|
432
|
+
ts.factory.createBlock(
|
|
433
|
+
[
|
|
434
|
+
ts.factory.createVariableStatement(
|
|
435
|
+
undefined,
|
|
436
|
+
ts.factory.createVariableDeclarationList(
|
|
437
|
+
[
|
|
438
|
+
ts.factory.createVariableDeclaration(
|
|
439
|
+
ts.factory.createIdentifier('params'),
|
|
440
|
+
undefined,
|
|
441
|
+
undefined,
|
|
442
|
+
ts.factory.createNewExpression(
|
|
443
|
+
ts.factory.createIdentifier('URLSearchParams'),
|
|
444
|
+
undefined,
|
|
445
|
+
[],
|
|
446
|
+
),
|
|
447
|
+
),
|
|
448
|
+
],
|
|
449
|
+
ts.NodeFlags.Const,
|
|
450
|
+
),
|
|
451
|
+
),
|
|
452
|
+
ts.factory.createExpressionStatement(
|
|
453
|
+
ts.factory.createCallExpression(
|
|
454
|
+
ts.factory.createPropertyAccessExpression(
|
|
455
|
+
ts.factory.createCallExpression(
|
|
456
|
+
ts.factory.createPropertyAccessExpression(
|
|
457
|
+
ts.factory.createIdentifier('Object'),
|
|
458
|
+
ts.factory.createIdentifier('entries'),
|
|
459
|
+
),
|
|
460
|
+
undefined,
|
|
461
|
+
[
|
|
462
|
+
ts.factory.createPropertyAccessExpression(
|
|
463
|
+
ts.factory.createIdentifier('options'),
|
|
464
|
+
ts.factory.createIdentifier('data'),
|
|
465
|
+
),
|
|
466
|
+
],
|
|
467
|
+
),
|
|
468
|
+
ts.factory.createIdentifier('forEach'),
|
|
469
|
+
),
|
|
470
|
+
undefined,
|
|
471
|
+
[
|
|
472
|
+
ts.factory.createArrowFunction(
|
|
473
|
+
undefined,
|
|
474
|
+
undefined,
|
|
475
|
+
[
|
|
476
|
+
ts.factory.createParameterDeclaration(
|
|
477
|
+
undefined,
|
|
478
|
+
undefined,
|
|
479
|
+
ts.factory.createArrayBindingPattern([
|
|
480
|
+
ts.factory.createBindingElement(
|
|
481
|
+
undefined,
|
|
482
|
+
undefined,
|
|
483
|
+
ts.factory.createIdentifier('key'),
|
|
484
|
+
undefined,
|
|
485
|
+
),
|
|
486
|
+
ts.factory.createBindingElement(
|
|
487
|
+
undefined,
|
|
488
|
+
undefined,
|
|
489
|
+
ts.factory.createIdentifier('value'),
|
|
490
|
+
undefined,
|
|
491
|
+
),
|
|
492
|
+
]),
|
|
493
|
+
undefined,
|
|
494
|
+
undefined,
|
|
495
|
+
),
|
|
496
|
+
],
|
|
497
|
+
undefined,
|
|
498
|
+
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
|
499
|
+
ts.factory.createBlock(
|
|
500
|
+
[
|
|
501
|
+
ts.factory.createExpressionStatement(
|
|
502
|
+
ts.factory.createCallExpression(
|
|
503
|
+
ts.factory.createPropertyAccessExpression(
|
|
504
|
+
ts.factory.createIdentifier('params'),
|
|
505
|
+
ts.factory.createIdentifier('set'),
|
|
506
|
+
),
|
|
507
|
+
undefined,
|
|
508
|
+
[
|
|
509
|
+
ts.factory.createIdentifier('key'),
|
|
510
|
+
ts.factory.createCallExpression(
|
|
511
|
+
ts.factory.createIdentifier('String'),
|
|
512
|
+
undefined,
|
|
513
|
+
[ts.factory.createIdentifier('value')],
|
|
514
|
+
),
|
|
515
|
+
],
|
|
516
|
+
),
|
|
517
|
+
),
|
|
518
|
+
],
|
|
519
|
+
false,
|
|
520
|
+
),
|
|
521
|
+
),
|
|
522
|
+
],
|
|
523
|
+
),
|
|
524
|
+
),
|
|
525
|
+
ts.factory.createReturnStatement(
|
|
526
|
+
ts.factory.createCallExpression(
|
|
527
|
+
ts.factory.createPropertyAccessExpression(
|
|
528
|
+
ts.factory.createIdentifier('params'),
|
|
529
|
+
ts.factory.createIdentifier('toString'),
|
|
530
|
+
),
|
|
531
|
+
undefined,
|
|
532
|
+
[],
|
|
533
|
+
),
|
|
534
|
+
),
|
|
535
|
+
],
|
|
536
|
+
false,
|
|
537
|
+
),
|
|
538
|
+
),
|
|
539
|
+
),
|
|
540
|
+
undefined,
|
|
541
|
+
[],
|
|
542
|
+
),
|
|
543
|
+
undefined,
|
|
544
|
+
// JSON: stringify the data
|
|
545
|
+
ts.factory.createCallExpression(
|
|
546
|
+
ts.factory.createPropertyAccessExpression(
|
|
547
|
+
ts.factory.createIdentifier('JSON'),
|
|
548
|
+
ts.factory.createIdentifier('stringify'),
|
|
549
|
+
),
|
|
550
|
+
undefined,
|
|
551
|
+
[
|
|
552
|
+
ts.factory.createPropertyAccessExpression(
|
|
553
|
+
ts.factory.createIdentifier('options'),
|
|
554
|
+
ts.factory.createIdentifier('data'),
|
|
555
|
+
),
|
|
556
|
+
],
|
|
557
|
+
),
|
|
558
|
+
),
|
|
559
|
+
undefined,
|
|
560
|
+
ts.factory.createNull(),
|
|
561
|
+
),
|
|
562
|
+
),
|
|
563
|
+
],
|
|
564
|
+
ts.NodeFlags.Const,
|
|
565
|
+
),
|
|
566
|
+
),
|
|
567
|
+
// Make fetch request
|
|
194
568
|
ts.factory.createVariableStatement(
|
|
195
569
|
undefined,
|
|
196
570
|
ts.factory.createVariableDeclarationList(
|
|
@@ -210,15 +584,11 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
|
|
|
210
584
|
),
|
|
211
585
|
ts.factory.createPropertyAssignment(
|
|
212
586
|
ts.factory.createIdentifier('headers'),
|
|
213
|
-
ts.factory.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
),
|
|
219
|
-
],
|
|
220
|
-
true,
|
|
221
|
-
),
|
|
587
|
+
ts.factory.createIdentifier('headers'),
|
|
588
|
+
),
|
|
589
|
+
ts.factory.createPropertyAssignment(
|
|
590
|
+
ts.factory.createIdentifier('body'),
|
|
591
|
+
ts.factory.createIdentifier('body'),
|
|
222
592
|
),
|
|
223
593
|
],
|
|
224
594
|
true,
|
|
@@ -230,6 +600,7 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
|
|
|
230
600
|
ts.NodeFlags.Const,
|
|
231
601
|
),
|
|
232
602
|
),
|
|
603
|
+
// Check response status
|
|
233
604
|
ts.factory.createIfStatement(
|
|
234
605
|
ts.factory.createPrefixUnaryExpression(
|
|
235
606
|
ts.SyntaxKind.ExclamationToken,
|
|
@@ -260,6 +631,7 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
|
|
|
260
631
|
),
|
|
261
632
|
undefined,
|
|
262
633
|
),
|
|
634
|
+
// Return parsed JSON
|
|
263
635
|
ts.factory.createReturnStatement(
|
|
264
636
|
ts.factory.createAwaitExpression(
|
|
265
637
|
ts.factory.createCallExpression(
|
|
@@ -306,8 +678,84 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
|
|
|
306
678
|
schema: MethodSchemaType,
|
|
307
679
|
schemas: Record<string, ts.VariableStatement>,
|
|
308
680
|
): ts.MethodDeclaration {
|
|
309
|
-
const parameters = this.buildMethodParameters(
|
|
681
|
+
const {parameters, pathParams, queryParams, hasRequestBody, contentType} = this.buildMethodParameters(
|
|
682
|
+
schema,
|
|
683
|
+
schemas,
|
|
684
|
+
);
|
|
310
685
|
const responseType = this.getResponseType(schema, schemas);
|
|
686
|
+
const responseSchema = this.getResponseSchema(schema, schemas);
|
|
687
|
+
|
|
688
|
+
const statements: ts.Statement[] = [];
|
|
689
|
+
|
|
690
|
+
// Build path with parameter substitution
|
|
691
|
+
const pathExpression = this.buildPathExpression(path, pathParams);
|
|
692
|
+
|
|
693
|
+
// Build query parameters object
|
|
694
|
+
const queryParamsExpression: ts.Expression | undefined =
|
|
695
|
+
queryParams.length > 0
|
|
696
|
+
? ts.factory.createObjectLiteralExpression(
|
|
697
|
+
queryParams.map((param) => {
|
|
698
|
+
const paramName = this.typeBuilder.sanitizeIdentifier(param.name);
|
|
699
|
+
return ts.factory.createPropertyAssignment(
|
|
700
|
+
ts.factory.createStringLiteral(param.name, true),
|
|
701
|
+
ts.factory.createIdentifier(paramName),
|
|
702
|
+
);
|
|
703
|
+
}),
|
|
704
|
+
false,
|
|
705
|
+
)
|
|
706
|
+
: undefined;
|
|
707
|
+
|
|
708
|
+
// Build request body
|
|
709
|
+
const requestBodyExpression: ts.Expression | undefined = hasRequestBody
|
|
710
|
+
? ts.factory.createIdentifier('body')
|
|
711
|
+
: undefined;
|
|
712
|
+
|
|
713
|
+
// Build options object for makeRequest
|
|
714
|
+
const optionsProps: ts.ObjectLiteralElementLike[] = [];
|
|
715
|
+
if (queryParamsExpression) {
|
|
716
|
+
optionsProps.push(
|
|
717
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier('params'), queryParamsExpression),
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
if (requestBodyExpression) {
|
|
721
|
+
optionsProps.push(
|
|
722
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier('data'), requestBodyExpression),
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
// Add content type if it's form-urlencoded
|
|
726
|
+
if (hasRequestBody && contentType === 'application/x-www-form-urlencoded') {
|
|
727
|
+
optionsProps.push(
|
|
728
|
+
ts.factory.createPropertyAssignment(
|
|
729
|
+
ts.factory.createIdentifier('contentType'),
|
|
730
|
+
ts.factory.createStringLiteral('application/x-www-form-urlencoded', true),
|
|
731
|
+
),
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
const optionsExpression = ts.factory.createObjectLiteralExpression(optionsProps, false);
|
|
736
|
+
|
|
737
|
+
// Call makeRequest
|
|
738
|
+
const makeRequestCall = ts.factory.createCallExpression(
|
|
739
|
+
ts.factory.createPropertyAccessExpression(
|
|
740
|
+
ts.factory.createThis(),
|
|
741
|
+
ts.factory.createPrivateIdentifier('#makeRequest'),
|
|
742
|
+
),
|
|
743
|
+
undefined,
|
|
744
|
+
[ts.factory.createStringLiteral(method.toUpperCase(), true), pathExpression, optionsExpression],
|
|
745
|
+
);
|
|
746
|
+
|
|
747
|
+
// Add Zod validation if we have a response schema
|
|
748
|
+
if (responseSchema) {
|
|
749
|
+
const validateCall = ts.factory.createCallExpression(
|
|
750
|
+
ts.factory.createPropertyAccessExpression(responseSchema, ts.factory.createIdentifier('parse')),
|
|
751
|
+
undefined,
|
|
752
|
+
[ts.factory.createAwaitExpression(makeRequestCall)],
|
|
753
|
+
);
|
|
754
|
+
|
|
755
|
+
statements.push(ts.factory.createReturnStatement(validateCall));
|
|
756
|
+
} else {
|
|
757
|
+
statements.push(ts.factory.createReturnStatement(ts.factory.createAwaitExpression(makeRequestCall)));
|
|
758
|
+
}
|
|
311
759
|
|
|
312
760
|
return ts.factory.createMethodDeclaration(
|
|
313
761
|
[ts.factory.createToken(ts.SyntaxKind.AsyncKeyword)],
|
|
@@ -317,68 +765,287 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
|
|
|
317
765
|
undefined,
|
|
318
766
|
parameters,
|
|
319
767
|
responseType,
|
|
320
|
-
ts.factory.createBlock(
|
|
321
|
-
[
|
|
322
|
-
ts.factory.createReturnStatement(
|
|
323
|
-
ts.factory.createAwaitExpression(
|
|
324
|
-
ts.factory.createCallExpression(
|
|
325
|
-
ts.factory.createPropertyAccessExpression(
|
|
326
|
-
ts.factory.createThis(),
|
|
327
|
-
ts.factory.createPrivateIdentifier('#makeRequest'),
|
|
328
|
-
),
|
|
329
|
-
undefined,
|
|
330
|
-
[
|
|
331
|
-
ts.factory.createStringLiteral(method.toUpperCase(), true),
|
|
332
|
-
ts.factory.createStringLiteral(path, true),
|
|
333
|
-
],
|
|
334
|
-
),
|
|
335
|
-
),
|
|
336
|
-
),
|
|
337
|
-
],
|
|
338
|
-
true,
|
|
339
|
-
),
|
|
768
|
+
ts.factory.createBlock(statements, true),
|
|
340
769
|
);
|
|
341
770
|
}
|
|
342
771
|
|
|
772
|
+
private buildPathExpression(path: string, pathParams: {name: string; type: string}[]): ts.Expression {
|
|
773
|
+
// Replace {param} with ${param} for template literal
|
|
774
|
+
const pathParamNames = new Set(pathParams.map((p) => p.name));
|
|
775
|
+
const pathParamRegex = /\{([^}]+)\}/g;
|
|
776
|
+
const matches: {index: number; length: number; name: string}[] = [];
|
|
777
|
+
|
|
778
|
+
// Find all path parameters
|
|
779
|
+
for (const match of path.matchAll(pathParamRegex)) {
|
|
780
|
+
const paramName = match[1];
|
|
781
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
782
|
+
if (paramName && pathParamNames.has(paramName) && match.index !== undefined) {
|
|
783
|
+
matches.push({
|
|
784
|
+
index: match.index,
|
|
785
|
+
length: match[0].length,
|
|
786
|
+
name: paramName,
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
if (matches.length === 0) {
|
|
792
|
+
// No path parameters, return as string literal
|
|
793
|
+
return ts.factory.createStringLiteral(path, true);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// Build template expression
|
|
797
|
+
const templateSpans: ts.TemplateSpan[] = [];
|
|
798
|
+
let lastIndex = 0;
|
|
799
|
+
|
|
800
|
+
for (const [index, m] of matches.entries()) {
|
|
801
|
+
const before = path.substring(lastIndex, m.index);
|
|
802
|
+
const sanitizedName = this.typeBuilder.sanitizeIdentifier(m.name);
|
|
803
|
+
const isLast = index === matches.length - 1;
|
|
804
|
+
const after = isLast ? path.substring(m.index + m.length) : '';
|
|
805
|
+
|
|
806
|
+
if (isLast) {
|
|
807
|
+
templateSpans.push(
|
|
808
|
+
ts.factory.createTemplateSpan(
|
|
809
|
+
ts.factory.createIdentifier(sanitizedName),
|
|
810
|
+
ts.factory.createTemplateTail(after, after),
|
|
811
|
+
),
|
|
812
|
+
);
|
|
813
|
+
} else {
|
|
814
|
+
templateSpans.push(
|
|
815
|
+
ts.factory.createTemplateSpan(
|
|
816
|
+
ts.factory.createIdentifier(sanitizedName),
|
|
817
|
+
ts.factory.createTemplateMiddle(before, before),
|
|
818
|
+
),
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
lastIndex = m.index + m.length;
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
const firstMatch = matches[0];
|
|
826
|
+
if (!firstMatch) {
|
|
827
|
+
return ts.factory.createStringLiteral(path, true);
|
|
828
|
+
}
|
|
829
|
+
const head = path.substring(0, firstMatch.index);
|
|
830
|
+
return ts.factory.createTemplateExpression(ts.factory.createTemplateHead(head, head), templateSpans);
|
|
831
|
+
}
|
|
832
|
+
|
|
343
833
|
private buildMethodParameters(
|
|
344
834
|
schema: MethodSchemaType,
|
|
345
835
|
schemas: Record<string, ts.VariableStatement>,
|
|
346
|
-
):
|
|
347
|
-
|
|
836
|
+
): {
|
|
837
|
+
parameters: ts.ParameterDeclaration[];
|
|
838
|
+
pathParams: {name: string; type: string}[];
|
|
839
|
+
queryParams: {name: string; type: string; required: boolean}[];
|
|
840
|
+
hasRequestBody: boolean;
|
|
841
|
+
contentType: string;
|
|
842
|
+
} {
|
|
348
843
|
const parameters: ts.ParameterDeclaration[] = [];
|
|
844
|
+
const pathParams: {name: string; type: string}[] = [];
|
|
845
|
+
const queryParams: {name: string; type: string; required: boolean}[] = [];
|
|
349
846
|
|
|
847
|
+
// Extract path and query parameters
|
|
350
848
|
if (schema.parameters) {
|
|
351
|
-
schema.parameters
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
849
|
+
for (const param of schema.parameters) {
|
|
850
|
+
const paramName = this.typeBuilder.sanitizeIdentifier(param.name);
|
|
851
|
+
const paramType = this.getParameterType(param.schema);
|
|
852
|
+
|
|
853
|
+
if (param.in === 'path') {
|
|
854
|
+
pathParams.push({name: param.name, type: paramType});
|
|
855
|
+
parameters.push(this.typeBuilder.createParameter(paramName, paramType, undefined, false));
|
|
856
|
+
} else if (param.in === 'query') {
|
|
857
|
+
// Improve type inference for query parameters
|
|
858
|
+
const queryParamType =
|
|
859
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
860
|
+
typeof param.schema === 'object' && param.schema !== null
|
|
861
|
+
? (() => {
|
|
862
|
+
const paramSchema = param.schema as {type?: string; items?: unknown; enum?: unknown[]};
|
|
863
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
864
|
+
if (paramSchema['type'] === 'array' && paramSchema['items']) {
|
|
865
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
866
|
+
const itemSchema = paramSchema['items'] as {type?: string};
|
|
867
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
868
|
+
if (itemSchema['type'] === 'string') {
|
|
869
|
+
return 'string[]' as const;
|
|
870
|
+
}
|
|
871
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
872
|
+
if (itemSchema['type'] === 'number' || itemSchema['type'] === 'integer') {
|
|
873
|
+
return 'number[]' as const;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
return paramType;
|
|
877
|
+
})()
|
|
878
|
+
: paramType;
|
|
879
|
+
queryParams.push({name: param.name, type: queryParamType, required: param.required ?? false});
|
|
880
|
+
parameters.push(this.typeBuilder.createParameter(paramName, queryParamType, undefined, !param.required));
|
|
361
881
|
}
|
|
362
|
-
}
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// Add request body parameter if present
|
|
886
|
+
// Check for both application/json and application/x-www-form-urlencoded
|
|
887
|
+
const jsonContent = schema.requestBody?.content?.['application/json'];
|
|
888
|
+
const formContent = schema.requestBody?.content?.['application/x-www-form-urlencoded'];
|
|
889
|
+
const hasRequestBody = !!(jsonContent ?? formContent);
|
|
890
|
+
|
|
891
|
+
if (hasRequestBody) {
|
|
892
|
+
const requestBodyContent = jsonContent ?? formContent;
|
|
893
|
+
const requestBodySchema = requestBodyContent?.schema;
|
|
894
|
+
const bodyType =
|
|
895
|
+
typeof requestBodySchema === 'object' && requestBodySchema !== null
|
|
896
|
+
? (() => {
|
|
897
|
+
const schemaObj = requestBodySchema as Record<string, unknown>;
|
|
898
|
+
const ref = schemaObj['$ref'];
|
|
899
|
+
|
|
900
|
+
if (ref && typeof ref === 'string' && ref.startsWith('#/components/schemas/')) {
|
|
901
|
+
const refName = ref.split('/').pop() ?? 'unknown';
|
|
902
|
+
return this.typeBuilder.sanitizeIdentifier(refName);
|
|
903
|
+
}
|
|
904
|
+
// Fallback to getSchemaTypeName for non-ref schemas
|
|
905
|
+
return this.getSchemaTypeName(requestBodySchema, schemas);
|
|
906
|
+
})()
|
|
907
|
+
: 'unknown';
|
|
908
|
+
|
|
909
|
+
parameters.push(this.typeBuilder.createParameter('body', bodyType, undefined, !schema.requestBody?.required));
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// Determine content type for request body
|
|
913
|
+
const contentType =
|
|
914
|
+
hasRequestBody && schema.requestBody?.content?.['application/x-www-form-urlencoded']
|
|
915
|
+
? 'application/x-www-form-urlencoded'
|
|
916
|
+
: 'application/json';
|
|
917
|
+
|
|
918
|
+
return {parameters, pathParams, queryParams, hasRequestBody, contentType};
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
private getParameterType(schema: unknown): string {
|
|
922
|
+
if (!schema || typeof schema !== 'object') {
|
|
923
|
+
return 'string';
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
const schemaObj = schema as {
|
|
927
|
+
type?: string;
|
|
928
|
+
format?: string;
|
|
929
|
+
$ref?: string;
|
|
930
|
+
enum?: unknown[];
|
|
931
|
+
items?: unknown;
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
if (schemaObj.$ref) {
|
|
935
|
+
const refName = schemaObj.$ref.split('/').pop() ?? 'unknown';
|
|
936
|
+
return this.typeBuilder.sanitizeIdentifier(refName);
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
if (schemaObj.type === 'array' && schemaObj.items) {
|
|
940
|
+
const itemType = this.getParameterType(schemaObj.items);
|
|
941
|
+
return `${itemType}[]`;
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
switch (schemaObj.type) {
|
|
945
|
+
case 'integer':
|
|
946
|
+
case 'number':
|
|
947
|
+
return 'number';
|
|
948
|
+
case 'boolean':
|
|
949
|
+
return 'boolean';
|
|
950
|
+
case 'array':
|
|
951
|
+
return 'unknown[]';
|
|
952
|
+
case 'object':
|
|
953
|
+
return 'Record<string, unknown>';
|
|
954
|
+
case 'string':
|
|
955
|
+
// If it has enum values, we could generate a union type, but for simplicity, keep as string
|
|
956
|
+
// The Zod schema will handle the validation
|
|
957
|
+
return 'string';
|
|
958
|
+
default:
|
|
959
|
+
return 'string';
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
private getSchemaTypeName(schema: unknown, _schemas: Record<string, ts.VariableStatement>): string {
|
|
964
|
+
if (typeof schema !== 'object' || schema === null) {
|
|
965
|
+
return 'unknown';
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
const schemaObj = schema as {$ref?: string; type?: string; items?: unknown};
|
|
969
|
+
|
|
970
|
+
// Check for $ref using both dot notation and bracket notation
|
|
971
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
972
|
+
const ref = schemaObj['$ref'];
|
|
973
|
+
if (ref && typeof ref === 'string') {
|
|
974
|
+
const refName = ref.split('/').pop() ?? 'unknown';
|
|
975
|
+
return this.typeBuilder.sanitizeIdentifier(refName);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
if (schemaObj.type === 'array' && schemaObj.items) {
|
|
979
|
+
const itemType = this.getSchemaTypeName(schemaObj.items, _schemas);
|
|
980
|
+
return `${itemType}[]`;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
switch (schemaObj.type) {
|
|
984
|
+
case 'integer':
|
|
985
|
+
case 'number':
|
|
986
|
+
return 'number';
|
|
987
|
+
case 'boolean':
|
|
988
|
+
return 'boolean';
|
|
989
|
+
case 'string':
|
|
990
|
+
return 'string';
|
|
991
|
+
case 'object':
|
|
992
|
+
return 'Record<string, unknown>';
|
|
993
|
+
default:
|
|
994
|
+
return 'unknown';
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
private getResponseSchema(
|
|
999
|
+
schema: MethodSchemaType,
|
|
1000
|
+
_schemas: Record<string, ts.VariableStatement>,
|
|
1001
|
+
): ts.Identifier | undefined {
|
|
1002
|
+
// Try to find a 200 response first, then 201, then default
|
|
1003
|
+
const response200 = schema.responses?.['200'];
|
|
1004
|
+
const response201 = schema.responses?.['201'];
|
|
1005
|
+
const responseDefault = schema.responses?.['default'];
|
|
1006
|
+
|
|
1007
|
+
const response = response200 ?? response201 ?? responseDefault;
|
|
1008
|
+
if (!response?.content?.['application/json']?.schema) {
|
|
1009
|
+
return undefined;
|
|
363
1010
|
}
|
|
364
1011
|
|
|
365
|
-
|
|
1012
|
+
const responseSchema = response.content['application/json'].schema;
|
|
1013
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
1014
|
+
if (responseSchema !== null && typeof responseSchema === 'object' && '$ref' in responseSchema) {
|
|
1015
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
1016
|
+
const ref = (responseSchema as {$ref: string})['$ref'];
|
|
1017
|
+
if (typeof ref === 'string') {
|
|
1018
|
+
const refName = ref.split('/').pop() ?? 'unknown';
|
|
1019
|
+
return ts.factory.createIdentifier(this.typeBuilder.sanitizeIdentifier(refName));
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
366
1022
|
|
|
367
|
-
return
|
|
1023
|
+
// For inline schemas, we'd need to generate a schema, but for now return undefined
|
|
1024
|
+
// This could be enhanced to generate inline schemas
|
|
1025
|
+
return undefined;
|
|
368
1026
|
}
|
|
369
1027
|
|
|
370
1028
|
private getResponseType(
|
|
371
1029
|
schema: MethodSchemaType,
|
|
372
1030
|
schemas: Record<string, ts.VariableStatement>,
|
|
373
1031
|
): ts.TypeNode | undefined {
|
|
374
|
-
|
|
1032
|
+
// Try to find a 200 response first, then 201, then default
|
|
375
1033
|
const response200 = schema.responses?.['200'];
|
|
376
|
-
|
|
377
|
-
|
|
1034
|
+
const response201 = schema.responses?.['201'];
|
|
1035
|
+
const responseDefault = schema.responses?.['default'];
|
|
1036
|
+
|
|
1037
|
+
const response = response200 ?? response201 ?? responseDefault;
|
|
1038
|
+
if (!response?.content?.['application/json']?.schema) {
|
|
1039
|
+
return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
|
|
1040
|
+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword),
|
|
1041
|
+
]);
|
|
378
1042
|
}
|
|
379
1043
|
|
|
1044
|
+
const responseSchema = response.content['application/json'].schema;
|
|
1045
|
+
const typeName = this.getSchemaTypeName(responseSchema, schemas);
|
|
1046
|
+
|
|
380
1047
|
return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
|
|
381
|
-
ts.factory.
|
|
1048
|
+
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(typeName), undefined),
|
|
382
1049
|
]);
|
|
383
1050
|
}
|
|
384
1051
|
|
|
@@ -471,46 +1138,118 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
|
|
|
471
1138
|
const prop = safeProperty.data;
|
|
472
1139
|
|
|
473
1140
|
if (this.isReference(prop)) {
|
|
474
|
-
|
|
1141
|
+
const refSchema = this.buildFromReference(prop);
|
|
1142
|
+
return required
|
|
1143
|
+
? refSchema
|
|
1144
|
+
: ts.factory.createCallExpression(
|
|
1145
|
+
ts.factory.createPropertyAccessExpression(refSchema, ts.factory.createIdentifier('optional')),
|
|
1146
|
+
undefined,
|
|
1147
|
+
[],
|
|
1148
|
+
);
|
|
475
1149
|
}
|
|
476
1150
|
|
|
477
|
-
|
|
1151
|
+
if (prop['anyOf'] && Array.isArray(prop['anyOf']) && prop['anyOf'].length > 0) {
|
|
1152
|
+
return this.handleLogicalOperator('anyOf', prop['anyOf'], required);
|
|
1153
|
+
}
|
|
478
1154
|
|
|
479
|
-
if (prop
|
|
480
|
-
return this.handleLogicalOperator('
|
|
1155
|
+
if (prop['oneOf'] && Array.isArray(prop['oneOf']) && prop['oneOf'].length > 0) {
|
|
1156
|
+
return this.handleLogicalOperator('oneOf', prop['oneOf'], required);
|
|
481
1157
|
}
|
|
482
1158
|
|
|
483
|
-
if (prop
|
|
484
|
-
return this.handleLogicalOperator('
|
|
1159
|
+
if (prop['allOf'] && Array.isArray(prop['allOf']) && prop['allOf'].length > 0) {
|
|
1160
|
+
return this.handleLogicalOperator('allOf', prop['allOf'], required);
|
|
485
1161
|
}
|
|
486
1162
|
|
|
487
|
-
if (prop
|
|
488
|
-
return this.handleLogicalOperator('
|
|
1163
|
+
if (prop['not']) {
|
|
1164
|
+
return this.handleLogicalOperator('not', [prop['not']], required);
|
|
489
1165
|
}
|
|
490
1166
|
|
|
491
|
-
|
|
492
|
-
|
|
1167
|
+
// Handle enum
|
|
1168
|
+
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))),
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
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
|
+
|
|
1198
|
+
return required
|
|
1199
|
+
? enumExpression
|
|
1200
|
+
: ts.factory.createCallExpression(
|
|
1201
|
+
ts.factory.createPropertyAccessExpression(enumExpression, ts.factory.createIdentifier('optional')),
|
|
1202
|
+
undefined,
|
|
1203
|
+
[],
|
|
1204
|
+
);
|
|
493
1205
|
}
|
|
494
1206
|
|
|
495
|
-
switch (prop
|
|
496
|
-
case 'array':
|
|
497
|
-
|
|
1207
|
+
switch (prop['type']) {
|
|
1208
|
+
case 'array': {
|
|
1209
|
+
const itemsSchema = prop['items'] ? this.buildProperty(prop['items'], true) : this.buildZodAST(['unknown']);
|
|
1210
|
+
let arraySchema = this.buildZodAST([
|
|
498
1211
|
{
|
|
499
1212
|
type: 'array',
|
|
500
|
-
args:
|
|
1213
|
+
args: [itemsSchema],
|
|
501
1214
|
},
|
|
502
|
-
...(!required ? ['optional'] : []),
|
|
503
1215
|
]);
|
|
1216
|
+
|
|
1217
|
+
// Apply array constraints
|
|
1218
|
+
if (typeof prop['minItems'] === 'number') {
|
|
1219
|
+
arraySchema = ts.factory.createCallExpression(
|
|
1220
|
+
ts.factory.createPropertyAccessExpression(arraySchema, ts.factory.createIdentifier('min')),
|
|
1221
|
+
undefined,
|
|
1222
|
+
[ts.factory.createNumericLiteral(String(prop['minItems']))],
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1225
|
+
if (typeof prop['maxItems'] === 'number') {
|
|
1226
|
+
arraySchema = ts.factory.createCallExpression(
|
|
1227
|
+
ts.factory.createPropertyAccessExpression(arraySchema, ts.factory.createIdentifier('max')),
|
|
1228
|
+
undefined,
|
|
1229
|
+
[ts.factory.createNumericLiteral(String(prop['maxItems']))],
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
return required
|
|
1234
|
+
? arraySchema
|
|
1235
|
+
: ts.factory.createCallExpression(
|
|
1236
|
+
ts.factory.createPropertyAccessExpression(arraySchema, ts.factory.createIdentifier('optional')),
|
|
1237
|
+
undefined,
|
|
1238
|
+
[],
|
|
1239
|
+
);
|
|
1240
|
+
}
|
|
504
1241
|
case 'object': {
|
|
505
|
-
const
|
|
1242
|
+
const propObj = prop satisfies {
|
|
506
1243
|
properties?: Record<string, unknown>;
|
|
507
1244
|
required?: string[];
|
|
508
1245
|
};
|
|
1246
|
+
const properties = (propObj['properties'] ?? {}) as Record<string, unknown>;
|
|
1247
|
+
const propRequired = (propObj['required'] ?? []) as string[];
|
|
509
1248
|
|
|
510
1249
|
const propertiesEntries = Object.entries(properties);
|
|
511
1250
|
|
|
512
1251
|
if (propertiesEntries.length > 0) {
|
|
513
|
-
|
|
1252
|
+
const objectSchema = this.buildZodAST([
|
|
514
1253
|
{
|
|
515
1254
|
type: 'object',
|
|
516
1255
|
args: [
|
|
@@ -525,8 +1264,94 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
|
|
|
525
1264
|
),
|
|
526
1265
|
],
|
|
527
1266
|
},
|
|
528
|
-
...(!required ? ['optional'] : []),
|
|
529
1267
|
]);
|
|
1268
|
+
|
|
1269
|
+
// Apply object constraints
|
|
1270
|
+
let constrainedSchema = objectSchema;
|
|
1271
|
+
if (typeof prop['minProperties'] === 'number') {
|
|
1272
|
+
constrainedSchema = ts.factory.createCallExpression(
|
|
1273
|
+
ts.factory.createPropertyAccessExpression(constrainedSchema, ts.factory.createIdentifier('refine')),
|
|
1274
|
+
undefined,
|
|
1275
|
+
[
|
|
1276
|
+
ts.factory.createArrowFunction(
|
|
1277
|
+
undefined,
|
|
1278
|
+
undefined,
|
|
1279
|
+
[ts.factory.createParameterDeclaration(undefined, undefined, 'obj', undefined, undefined, undefined)],
|
|
1280
|
+
undefined,
|
|
1281
|
+
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
|
1282
|
+
ts.factory.createBinaryExpression(
|
|
1283
|
+
ts.factory.createPropertyAccessExpression(
|
|
1284
|
+
ts.factory.createCallExpression(
|
|
1285
|
+
ts.factory.createPropertyAccessExpression(
|
|
1286
|
+
ts.factory.createIdentifier('Object'),
|
|
1287
|
+
ts.factory.createIdentifier('keys'),
|
|
1288
|
+
),
|
|
1289
|
+
undefined,
|
|
1290
|
+
[ts.factory.createIdentifier('obj')],
|
|
1291
|
+
),
|
|
1292
|
+
ts.factory.createIdentifier('length'),
|
|
1293
|
+
),
|
|
1294
|
+
ts.factory.createToken(ts.SyntaxKind.GreaterThanEqualsToken),
|
|
1295
|
+
ts.factory.createNumericLiteral(String(prop['minProperties'])),
|
|
1296
|
+
),
|
|
1297
|
+
),
|
|
1298
|
+
ts.factory.createObjectLiteralExpression([
|
|
1299
|
+
ts.factory.createPropertyAssignment(
|
|
1300
|
+
ts.factory.createIdentifier('message'),
|
|
1301
|
+
ts.factory.createStringLiteral(
|
|
1302
|
+
`Object must have at least ${String(prop['minProperties'])} properties`,
|
|
1303
|
+
),
|
|
1304
|
+
),
|
|
1305
|
+
]),
|
|
1306
|
+
],
|
|
1307
|
+
);
|
|
1308
|
+
}
|
|
1309
|
+
if (typeof prop['maxProperties'] === 'number') {
|
|
1310
|
+
constrainedSchema = ts.factory.createCallExpression(
|
|
1311
|
+
ts.factory.createPropertyAccessExpression(constrainedSchema, ts.factory.createIdentifier('refine')),
|
|
1312
|
+
undefined,
|
|
1313
|
+
[
|
|
1314
|
+
ts.factory.createArrowFunction(
|
|
1315
|
+
undefined,
|
|
1316
|
+
undefined,
|
|
1317
|
+
[ts.factory.createParameterDeclaration(undefined, undefined, 'obj', undefined, undefined, undefined)],
|
|
1318
|
+
undefined,
|
|
1319
|
+
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
|
1320
|
+
ts.factory.createBinaryExpression(
|
|
1321
|
+
ts.factory.createPropertyAccessExpression(
|
|
1322
|
+
ts.factory.createCallExpression(
|
|
1323
|
+
ts.factory.createPropertyAccessExpression(
|
|
1324
|
+
ts.factory.createIdentifier('Object'),
|
|
1325
|
+
ts.factory.createIdentifier('keys'),
|
|
1326
|
+
),
|
|
1327
|
+
undefined,
|
|
1328
|
+
[ts.factory.createIdentifier('obj')],
|
|
1329
|
+
),
|
|
1330
|
+
ts.factory.createIdentifier('length'),
|
|
1331
|
+
),
|
|
1332
|
+
ts.factory.createToken(ts.SyntaxKind.LessThanEqualsToken),
|
|
1333
|
+
ts.factory.createNumericLiteral(String(prop['maxProperties'])),
|
|
1334
|
+
),
|
|
1335
|
+
),
|
|
1336
|
+
ts.factory.createObjectLiteralExpression([
|
|
1337
|
+
ts.factory.createPropertyAssignment(
|
|
1338
|
+
ts.factory.createIdentifier('message'),
|
|
1339
|
+
ts.factory.createStringLiteral(
|
|
1340
|
+
`Object must have at most ${String(prop['maxProperties'])} properties`,
|
|
1341
|
+
),
|
|
1342
|
+
),
|
|
1343
|
+
]),
|
|
1344
|
+
],
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
return required
|
|
1349
|
+
? constrainedSchema
|
|
1350
|
+
: ts.factory.createCallExpression(
|
|
1351
|
+
ts.factory.createPropertyAccessExpression(constrainedSchema, ts.factory.createIdentifier('optional')),
|
|
1352
|
+
undefined,
|
|
1353
|
+
[],
|
|
1354
|
+
);
|
|
530
1355
|
}
|
|
531
1356
|
|
|
532
1357
|
return this.buildZodAST([
|
|
@@ -536,21 +1361,319 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
|
|
|
536
1361
|
},
|
|
537
1362
|
]);
|
|
538
1363
|
}
|
|
539
|
-
case 'integer':
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
1364
|
+
case 'integer': {
|
|
1365
|
+
let numberSchema = this.buildZodAST(['number', 'int']);
|
|
1366
|
+
|
|
1367
|
+
// Apply number constraints
|
|
1368
|
+
if (prop['minimum'] !== undefined && typeof prop['minimum'] === 'number') {
|
|
1369
|
+
const minValue =
|
|
1370
|
+
prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean'
|
|
1371
|
+
? prop['minimum'] + 1
|
|
1372
|
+
: prop['minimum'];
|
|
1373
|
+
numberSchema = ts.factory.createCallExpression(
|
|
1374
|
+
ts.factory.createPropertyAccessExpression(
|
|
1375
|
+
numberSchema,
|
|
1376
|
+
ts.factory.createIdentifier(
|
|
1377
|
+
prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean' ? 'gt' : 'gte',
|
|
1378
|
+
),
|
|
1379
|
+
),
|
|
1380
|
+
undefined,
|
|
1381
|
+
[ts.factory.createNumericLiteral(String(minValue))],
|
|
1382
|
+
);
|
|
1383
|
+
}
|
|
1384
|
+
if (prop['maximum'] !== undefined && typeof prop['maximum'] === 'number') {
|
|
1385
|
+
const maxValue =
|
|
1386
|
+
prop['exclusiveMaximum'] && typeof prop['exclusiveMaximum'] === 'boolean'
|
|
1387
|
+
? prop['maximum'] - 1
|
|
1388
|
+
: prop['maximum'];
|
|
1389
|
+
numberSchema = ts.factory.createCallExpression(
|
|
1390
|
+
ts.factory.createPropertyAccessExpression(
|
|
1391
|
+
numberSchema,
|
|
1392
|
+
ts.factory.createIdentifier(prop['exclusiveMaximum'] ? 'lt' : 'lte'),
|
|
1393
|
+
),
|
|
1394
|
+
undefined,
|
|
1395
|
+
[ts.factory.createNumericLiteral(String(maxValue))],
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
if (typeof prop['multipleOf'] === 'number') {
|
|
1399
|
+
const refineFunction = ts.factory.createArrowFunction(
|
|
1400
|
+
undefined,
|
|
1401
|
+
undefined,
|
|
1402
|
+
[ts.factory.createParameterDeclaration(undefined, undefined, 'val', undefined, undefined, undefined)],
|
|
1403
|
+
undefined,
|
|
1404
|
+
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
|
1405
|
+
ts.factory.createBinaryExpression(
|
|
1406
|
+
ts.factory.createBinaryExpression(
|
|
1407
|
+
ts.factory.createIdentifier('val'),
|
|
1408
|
+
ts.factory.createToken(ts.SyntaxKind.PercentToken),
|
|
1409
|
+
ts.factory.createNumericLiteral(String(prop['multipleOf'])),
|
|
1410
|
+
),
|
|
1411
|
+
ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
|
|
1412
|
+
ts.factory.createNumericLiteral('0'),
|
|
1413
|
+
),
|
|
1414
|
+
);
|
|
1415
|
+
const refineOptions = ts.factory.createObjectLiteralExpression([
|
|
1416
|
+
ts.factory.createPropertyAssignment(
|
|
1417
|
+
ts.factory.createIdentifier('message'),
|
|
1418
|
+
ts.factory.createStringLiteral(`Number must be a multiple of ${String(prop['multipleOf'])}`),
|
|
1419
|
+
),
|
|
1420
|
+
]);
|
|
1421
|
+
numberSchema = ts.factory.createCallExpression(
|
|
1422
|
+
ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier('refine')),
|
|
1423
|
+
undefined,
|
|
1424
|
+
[refineFunction, refineOptions],
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
return required
|
|
1429
|
+
? numberSchema
|
|
1430
|
+
: ts.factory.createCallExpression(
|
|
1431
|
+
ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier('optional')),
|
|
1432
|
+
undefined,
|
|
1433
|
+
[],
|
|
1434
|
+
);
|
|
1435
|
+
}
|
|
1436
|
+
case 'number': {
|
|
1437
|
+
let numberSchema = this.buildZodAST(['number']);
|
|
1438
|
+
|
|
1439
|
+
// Apply number constraints
|
|
1440
|
+
if (prop['minimum'] !== undefined && typeof prop['minimum'] === 'number') {
|
|
1441
|
+
const minValue =
|
|
1442
|
+
prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean'
|
|
1443
|
+
? prop['minimum'] + 1
|
|
1444
|
+
: prop['minimum'];
|
|
1445
|
+
numberSchema = ts.factory.createCallExpression(
|
|
1446
|
+
ts.factory.createPropertyAccessExpression(
|
|
1447
|
+
numberSchema,
|
|
1448
|
+
ts.factory.createIdentifier(
|
|
1449
|
+
prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean' ? 'gt' : 'gte',
|
|
1450
|
+
),
|
|
1451
|
+
),
|
|
1452
|
+
undefined,
|
|
1453
|
+
[ts.factory.createNumericLiteral(String(minValue))],
|
|
1454
|
+
);
|
|
1455
|
+
}
|
|
1456
|
+
if (prop['maximum'] !== undefined && typeof prop['maximum'] === 'number') {
|
|
1457
|
+
const maxValue =
|
|
1458
|
+
prop['exclusiveMaximum'] && typeof prop['exclusiveMaximum'] === 'boolean'
|
|
1459
|
+
? prop['maximum'] - 1
|
|
1460
|
+
: prop['maximum'];
|
|
1461
|
+
numberSchema = ts.factory.createCallExpression(
|
|
1462
|
+
ts.factory.createPropertyAccessExpression(
|
|
1463
|
+
numberSchema,
|
|
1464
|
+
ts.factory.createIdentifier(prop['exclusiveMaximum'] ? 'lt' : 'lte'),
|
|
1465
|
+
),
|
|
1466
|
+
undefined,
|
|
1467
|
+
[ts.factory.createNumericLiteral(String(maxValue))],
|
|
1468
|
+
);
|
|
1469
|
+
}
|
|
1470
|
+
if (typeof prop['multipleOf'] === 'number') {
|
|
1471
|
+
const refineFunction = ts.factory.createArrowFunction(
|
|
1472
|
+
undefined,
|
|
1473
|
+
undefined,
|
|
1474
|
+
[ts.factory.createParameterDeclaration(undefined, undefined, 'val', undefined, undefined, undefined)],
|
|
1475
|
+
undefined,
|
|
1476
|
+
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
|
1477
|
+
ts.factory.createBinaryExpression(
|
|
1478
|
+
ts.factory.createBinaryExpression(
|
|
1479
|
+
ts.factory.createIdentifier('val'),
|
|
1480
|
+
ts.factory.createToken(ts.SyntaxKind.PercentToken),
|
|
1481
|
+
ts.factory.createNumericLiteral(String(prop['multipleOf'])),
|
|
1482
|
+
),
|
|
1483
|
+
ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken),
|
|
1484
|
+
ts.factory.createNumericLiteral('0'),
|
|
1485
|
+
),
|
|
1486
|
+
);
|
|
1487
|
+
const refineOptions = ts.factory.createObjectLiteralExpression([
|
|
1488
|
+
ts.factory.createPropertyAssignment(
|
|
1489
|
+
ts.factory.createIdentifier('message'),
|
|
1490
|
+
ts.factory.createStringLiteral(`Number must be a multiple of ${String(prop['multipleOf'])}`),
|
|
1491
|
+
),
|
|
1492
|
+
]);
|
|
1493
|
+
numberSchema = ts.factory.createCallExpression(
|
|
1494
|
+
ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier('refine')),
|
|
1495
|
+
undefined,
|
|
1496
|
+
[refineFunction, refineOptions],
|
|
1497
|
+
);
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
return required
|
|
1501
|
+
? numberSchema
|
|
1502
|
+
: ts.factory.createCallExpression(
|
|
1503
|
+
ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier('optional')),
|
|
1504
|
+
undefined,
|
|
1505
|
+
[],
|
|
1506
|
+
);
|
|
1507
|
+
}
|
|
1508
|
+
case 'string': {
|
|
1509
|
+
let stringSchema = this.buildZodAST(['string']);
|
|
1510
|
+
|
|
1511
|
+
// Apply string format
|
|
1512
|
+
if (prop['format']) {
|
|
1513
|
+
switch (prop['format']) {
|
|
1514
|
+
case 'email':
|
|
1515
|
+
stringSchema = ts.factory.createCallExpression(
|
|
1516
|
+
ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('email')),
|
|
1517
|
+
undefined,
|
|
1518
|
+
[],
|
|
1519
|
+
);
|
|
1520
|
+
break;
|
|
1521
|
+
case 'uri':
|
|
1522
|
+
case 'url':
|
|
1523
|
+
stringSchema = ts.factory.createCallExpression(
|
|
1524
|
+
ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('url')),
|
|
1525
|
+
undefined,
|
|
1526
|
+
[],
|
|
1527
|
+
);
|
|
1528
|
+
break;
|
|
1529
|
+
case 'uuid':
|
|
1530
|
+
stringSchema = ts.factory.createCallExpression(
|
|
1531
|
+
ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('uuid')),
|
|
1532
|
+
undefined,
|
|
1533
|
+
[],
|
|
1534
|
+
);
|
|
1535
|
+
break;
|
|
1536
|
+
case 'date-time':
|
|
1537
|
+
stringSchema = ts.factory.createCallExpression(
|
|
1538
|
+
ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('datetime')),
|
|
1539
|
+
undefined,
|
|
1540
|
+
[],
|
|
1541
|
+
);
|
|
1542
|
+
break;
|
|
1543
|
+
case 'date':
|
|
1544
|
+
stringSchema = ts.factory.createCallExpression(
|
|
1545
|
+
ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('date')),
|
|
1546
|
+
undefined,
|
|
1547
|
+
[],
|
|
1548
|
+
);
|
|
1549
|
+
break;
|
|
1550
|
+
case 'time':
|
|
1551
|
+
stringSchema = ts.factory.createCallExpression(
|
|
1552
|
+
ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('time')),
|
|
1553
|
+
undefined,
|
|
1554
|
+
[],
|
|
1555
|
+
);
|
|
1556
|
+
break;
|
|
1557
|
+
// Add more formats as needed
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
// Apply string constraints
|
|
1562
|
+
if (typeof prop['minLength'] === 'number') {
|
|
1563
|
+
stringSchema = ts.factory.createCallExpression(
|
|
1564
|
+
ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('min')),
|
|
1565
|
+
undefined,
|
|
1566
|
+
[ts.factory.createNumericLiteral(String(prop['minLength']))],
|
|
1567
|
+
);
|
|
1568
|
+
}
|
|
1569
|
+
if (typeof prop['maxLength'] === 'number') {
|
|
1570
|
+
stringSchema = ts.factory.createCallExpression(
|
|
1571
|
+
ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('max')),
|
|
1572
|
+
undefined,
|
|
1573
|
+
[ts.factory.createNumericLiteral(String(prop['maxLength']))],
|
|
1574
|
+
);
|
|
1575
|
+
}
|
|
1576
|
+
if (prop['pattern'] && typeof prop['pattern'] === 'string') {
|
|
1577
|
+
stringSchema = ts.factory.createCallExpression(
|
|
1578
|
+
ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('regex')),
|
|
1579
|
+
undefined,
|
|
1580
|
+
[
|
|
1581
|
+
ts.factory.createNewExpression(ts.factory.createIdentifier('RegExp'), undefined, [
|
|
1582
|
+
ts.factory.createStringLiteral(prop['pattern'], true),
|
|
1583
|
+
]),
|
|
1584
|
+
],
|
|
1585
|
+
);
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
// Apply default value if not required
|
|
1589
|
+
if (!required && prop['default'] !== undefined) {
|
|
1590
|
+
const defaultValue = this.buildDefaultValue(prop['default']);
|
|
1591
|
+
stringSchema = ts.factory.createCallExpression(
|
|
1592
|
+
ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('default')),
|
|
1593
|
+
undefined,
|
|
1594
|
+
[defaultValue],
|
|
1595
|
+
);
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
return required
|
|
1599
|
+
? stringSchema
|
|
1600
|
+
: ts.factory.createCallExpression(
|
|
1601
|
+
ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('optional')),
|
|
1602
|
+
undefined,
|
|
1603
|
+
[],
|
|
1604
|
+
);
|
|
1605
|
+
}
|
|
1606
|
+
case 'boolean': {
|
|
1607
|
+
let booleanSchema = this.buildZodAST(['boolean']);
|
|
1608
|
+
|
|
1609
|
+
// Apply default value if not required
|
|
1610
|
+
if (!required && prop['default'] !== undefined) {
|
|
1611
|
+
const defaultValue =
|
|
1612
|
+
typeof prop['default'] === 'boolean'
|
|
1613
|
+
? prop['default']
|
|
1614
|
+
? ts.factory.createTrue()
|
|
1615
|
+
: ts.factory.createFalse()
|
|
1616
|
+
: ts.factory.createFalse();
|
|
1617
|
+
booleanSchema = ts.factory.createCallExpression(
|
|
1618
|
+
ts.factory.createPropertyAccessExpression(booleanSchema, ts.factory.createIdentifier('default')),
|
|
1619
|
+
undefined,
|
|
1620
|
+
[defaultValue],
|
|
1621
|
+
);
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
return required
|
|
1625
|
+
? booleanSchema
|
|
1626
|
+
: ts.factory.createCallExpression(
|
|
1627
|
+
ts.factory.createPropertyAccessExpression(booleanSchema, ts.factory.createIdentifier('optional')),
|
|
1628
|
+
undefined,
|
|
1629
|
+
[],
|
|
1630
|
+
);
|
|
1631
|
+
}
|
|
548
1632
|
case 'unknown':
|
|
549
1633
|
default:
|
|
550
1634
|
return this.buildZodAST(['unknown', ...(!required ? ['optional'] : [])]);
|
|
551
1635
|
}
|
|
552
1636
|
}
|
|
553
1637
|
|
|
1638
|
+
private buildDefaultValue(value: unknown): ts.Expression {
|
|
1639
|
+
if (typeof value === 'string') {
|
|
1640
|
+
return ts.factory.createStringLiteral(value, true);
|
|
1641
|
+
}
|
|
1642
|
+
if (typeof value === 'number') {
|
|
1643
|
+
return ts.factory.createNumericLiteral(String(value));
|
|
1644
|
+
}
|
|
1645
|
+
if (typeof value === 'boolean') {
|
|
1646
|
+
return value ? ts.factory.createTrue() : ts.factory.createFalse();
|
|
1647
|
+
}
|
|
1648
|
+
if (value === null) {
|
|
1649
|
+
return ts.factory.createNull();
|
|
1650
|
+
}
|
|
1651
|
+
if (Array.isArray(value)) {
|
|
1652
|
+
return ts.factory.createArrayLiteralExpression(
|
|
1653
|
+
value.map((item) => this.buildDefaultValue(item)),
|
|
1654
|
+
false,
|
|
1655
|
+
);
|
|
1656
|
+
}
|
|
1657
|
+
if (typeof value === 'object') {
|
|
1658
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
1659
|
+
if (value === null) {
|
|
1660
|
+
return ts.factory.createNull();
|
|
1661
|
+
}
|
|
1662
|
+
return ts.factory.createObjectLiteralExpression(
|
|
1663
|
+
Object.entries(value).map(([key, val]) =>
|
|
1664
|
+
ts.factory.createPropertyAssignment(ts.factory.createIdentifier(key), this.buildDefaultValue(val)),
|
|
1665
|
+
),
|
|
1666
|
+
true,
|
|
1667
|
+
);
|
|
1668
|
+
}
|
|
1669
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
1670
|
+
return ts.factory.createStringLiteral(String(value), true);
|
|
1671
|
+
}
|
|
1672
|
+
// For objects and arrays, we need to handle them differently
|
|
1673
|
+
// This should not happen in practice, but we handle it for type safety
|
|
1674
|
+
return ts.factory.createStringLiteral(JSON.stringify(value), true);
|
|
1675
|
+
}
|
|
1676
|
+
|
|
554
1677
|
private handleLogicalOperator(
|
|
555
1678
|
operator: 'anyOf' | 'oneOf' | 'allOf' | 'not',
|
|
556
1679
|
schemas: unknown[],
|
|
@@ -641,6 +1764,7 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
|
|
|
641
1764
|
|
|
642
1765
|
private buildSchemaFromLogicalOperator(schema: unknown): ts.CallExpression | ts.Identifier {
|
|
643
1766
|
if (this.isReference(schema)) {
|
|
1767
|
+
// In logical operators, references are always required (they're part of a union/intersection)
|
|
644
1768
|
return this.buildFromReference(schema);
|
|
645
1769
|
}
|
|
646
1770
|
|
|
@@ -721,7 +1845,7 @@ export class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuil
|
|
|
721
1845
|
|
|
722
1846
|
private isReference(reference: unknown): reference is ReferenceType {
|
|
723
1847
|
if (typeof reference === 'object' && reference !== null && '$ref' in reference) {
|
|
724
|
-
const ref = reference
|
|
1848
|
+
const ref = reference satisfies {$ref?: unknown};
|
|
725
1849
|
return typeof ref.$ref === 'string' && ref.$ref.length > 0;
|
|
726
1850
|
}
|
|
727
1851
|
return false;
|