zod-codegen 1.6.3 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/.github/workflows/ci.yml +50 -48
  2. package/.github/workflows/release.yml +13 -3
  3. package/.husky/commit-msg +1 -1
  4. package/.husky/pre-commit +1 -1
  5. package/.lintstagedrc.json +5 -1
  6. package/.nvmrc +1 -1
  7. package/.prettierrc.json +12 -5
  8. package/CHANGELOG.md +11 -0
  9. package/CONTRIBUTING.md +12 -12
  10. package/EXAMPLES.md +135 -57
  11. package/PERFORMANCE.md +4 -4
  12. package/README.md +87 -64
  13. package/SECURITY.md +1 -1
  14. package/dist/src/cli.js +11 -18
  15. package/dist/src/generator.d.ts +2 -2
  16. package/dist/src/generator.d.ts.map +1 -1
  17. package/dist/src/generator.js +5 -3
  18. package/dist/src/interfaces/code-generator.d.ts.map +1 -1
  19. package/dist/src/services/code-generator.service.d.ts +3 -1
  20. package/dist/src/services/code-generator.service.d.ts.map +1 -1
  21. package/dist/src/services/code-generator.service.js +236 -219
  22. package/dist/src/services/file-reader.service.d.ts.map +1 -1
  23. package/dist/src/services/file-reader.service.js +1 -1
  24. package/dist/src/services/file-writer.service.d.ts.map +1 -1
  25. package/dist/src/services/file-writer.service.js +2 -2
  26. package/dist/src/services/import-builder.service.d.ts.map +1 -1
  27. package/dist/src/services/import-builder.service.js +3 -3
  28. package/dist/src/services/type-builder.service.d.ts.map +1 -1
  29. package/dist/src/types/generator-options.d.ts.map +1 -1
  30. package/dist/src/types/openapi.d.ts.map +1 -1
  31. package/dist/src/types/openapi.js +20 -20
  32. package/dist/src/utils/error-handler.d.ts.map +1 -1
  33. package/dist/src/utils/naming-convention.d.ts.map +1 -1
  34. package/dist/src/utils/naming-convention.js +6 -3
  35. package/dist/src/utils/signal-handler.d.ts.map +1 -1
  36. package/dist/tests/integration/cli-comprehensive.test.d.ts +2 -0
  37. package/dist/tests/integration/cli-comprehensive.test.d.ts.map +1 -0
  38. package/dist/tests/integration/cli-comprehensive.test.js +110 -0
  39. package/dist/tests/integration/cli.test.d.ts +2 -0
  40. package/dist/tests/integration/cli.test.d.ts.map +1 -0
  41. package/dist/tests/integration/cli.test.js +25 -0
  42. package/dist/tests/integration/error-scenarios.test.d.ts +2 -0
  43. package/dist/tests/integration/error-scenarios.test.d.ts.map +1 -0
  44. package/dist/tests/integration/error-scenarios.test.js +169 -0
  45. package/dist/tests/integration/snapshots.test.d.ts +2 -0
  46. package/dist/tests/integration/snapshots.test.d.ts.map +1 -0
  47. package/dist/tests/integration/snapshots.test.js +100 -0
  48. package/dist/tests/unit/code-generator-edge-cases.test.d.ts +2 -0
  49. package/dist/tests/unit/code-generator-edge-cases.test.d.ts.map +1 -0
  50. package/dist/tests/unit/code-generator-edge-cases.test.js +506 -0
  51. package/dist/tests/unit/code-generator.test.d.ts +2 -0
  52. package/dist/tests/unit/code-generator.test.d.ts.map +1 -0
  53. package/dist/tests/unit/code-generator.test.js +1364 -0
  54. package/dist/tests/unit/file-reader.test.d.ts +2 -0
  55. package/dist/tests/unit/file-reader.test.d.ts.map +1 -0
  56. package/dist/tests/unit/file-reader.test.js +125 -0
  57. package/dist/tests/unit/generator.test.d.ts +2 -0
  58. package/dist/tests/unit/generator.test.d.ts.map +1 -0
  59. package/dist/tests/unit/generator.test.js +119 -0
  60. package/dist/tests/unit/naming-convention.test.d.ts +2 -0
  61. package/dist/tests/unit/naming-convention.test.d.ts.map +1 -0
  62. package/dist/tests/unit/naming-convention.test.js +256 -0
  63. package/dist/tests/unit/reporter.test.d.ts +2 -0
  64. package/dist/tests/unit/reporter.test.d.ts.map +1 -0
  65. package/dist/tests/unit/reporter.test.js +44 -0
  66. package/dist/tests/unit/type-builder.test.d.ts +2 -0
  67. package/dist/tests/unit/type-builder.test.d.ts.map +1 -0
  68. package/dist/tests/unit/type-builder.test.js +108 -0
  69. package/dist/vitest.config.d.ts.map +1 -1
  70. package/dist/vitest.config.js +10 -20
  71. package/eslint.config.mjs +38 -28
  72. package/examples/.gitkeep +1 -1
  73. package/examples/README.md +4 -2
  74. package/examples/petstore/README.md +18 -17
  75. package/examples/petstore/{type.ts → api.ts} +158 -74
  76. package/examples/petstore/authenticated-usage.ts +6 -4
  77. package/examples/petstore/basic-usage.ts +4 -3
  78. package/examples/petstore/error-handling-usage.ts +84 -0
  79. package/examples/petstore/retry-handler-usage.ts +11 -18
  80. package/examples/petstore/server-variables-usage.ts +10 -10
  81. package/examples/pokeapi/README.md +8 -8
  82. package/examples/pokeapi/api.ts +218 -0
  83. package/examples/pokeapi/basic-usage.ts +3 -2
  84. package/examples/pokeapi/custom-client.ts +5 -4
  85. package/package.json +17 -21
  86. package/src/cli.ts +20 -25
  87. package/src/generator.ts +13 -11
  88. package/src/interfaces/code-generator.ts +1 -1
  89. package/src/services/code-generator.service.ts +799 -1120
  90. package/src/services/file-reader.service.ts +6 -5
  91. package/src/services/file-writer.service.ts +7 -7
  92. package/src/services/import-builder.service.ts +9 -13
  93. package/src/services/type-builder.service.ts +8 -19
  94. package/src/types/generator-options.ts +1 -1
  95. package/src/types/openapi.ts +22 -22
  96. package/src/utils/error-handler.ts +2 -2
  97. package/src/utils/naming-convention.ts +13 -10
  98. package/src/utils/reporter.ts +2 -2
  99. package/src/utils/signal-handler.ts +7 -8
  100. package/tests/integration/cli-comprehensive.test.ts +38 -32
  101. package/tests/integration/cli.test.ts +5 -5
  102. package/tests/integration/error-scenarios.test.ts +20 -26
  103. package/tests/integration/snapshots.test.ts +19 -23
  104. package/tests/unit/code-generator-edge-cases.test.ts +133 -133
  105. package/tests/unit/code-generator.test.ts +431 -330
  106. package/tests/unit/file-reader.test.ts +14 -14
  107. package/tests/unit/generator.test.ts +30 -18
  108. package/tests/unit/naming-convention.test.ts +27 -27
  109. package/tests/unit/type-builder.test.ts +2 -2
  110. package/tsconfig.json +5 -3
  111. package/vitest.config.ts +11 -21
  112. package/dist/scripts/update-manifest.d.ts +0 -14
  113. package/dist/scripts/update-manifest.d.ts.map +0 -1
  114. package/dist/scripts/update-manifest.js +0 -33
  115. package/dist/src/assets/manifest.json +0 -5
  116. package/examples/pokeapi/type.ts +0 -109
  117. package/generated/type.ts +0 -371
  118. package/scripts/update-manifest.ts +0 -49
  119. package/src/assets/manifest.json +0 -5
@@ -1 +1 @@
1
- {"version":3,"file":"file-reader.service.d.ts","sourceRoot":"","sources":["../../../src/services/file-reader.service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,iBAAiB,EAAE,iBAAiB,EAAC,MAAM,2BAA2B,CAAC;AACpF,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAGtD,qBAAa,qBAAsB,YAAW,iBAAiB;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAe9C;AAED,qBAAa,wBAAyB,YAAW,iBAAiB,CAAC,eAAe,CAAC;IACjF,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe;CAgBvC"}
1
+ {"version":3,"file":"file-reader.service.d.ts","sourceRoot":"","sources":["../../../src/services/file-reader.service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACtF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGxD,qBAAa,qBAAsB,YAAW,iBAAiB;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAgB9C;AAED,qBAAa,wBAAyB,YAAW,iBAAiB,CAAC,eAAe,CAAC;IACjF,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe;CAgBvC"}
@@ -1,5 +1,5 @@
1
- import { readFileSync } from 'node:fs';
2
1
  import { load } from 'js-yaml';
2
+ import { readFileSync } from 'node:fs';
3
3
  import { OpenApiSpec } from '../types/openapi.js';
4
4
  export class SyncFileReaderService {
5
5
  async readFile(path) {
@@ -1 +1 @@
1
- {"version":3,"file":"file-writer.service.d.ts","sourceRoot":"","sources":["../../../src/services/file-writer.service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,8BAA8B,CAAC;AAE7D,qBAAa,qBAAsB,YAAW,UAAU;IAEpD,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAFT,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM;IAGpC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAqBlD,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,SAAY,GAAG,MAAM;CAGnE"}
1
+ {"version":3,"file":"file-writer.service.d.ts","sourceRoot":"","sources":["../../../src/services/file-writer.service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAE/D,qBAAa,qBAAsB,YAAW,UAAU;IAEpD,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAFT,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM;IAGpC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAqBlD,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,SAAW,GAAG,MAAM;CAGlE"}
@@ -18,7 +18,7 @@ export class SyncFileWriterService {
18
18
  '/* eslint-disable */',
19
19
  '// @ts-nocheck',
20
20
  '',
21
- content,
21
+ content
22
22
  ].join('\n');
23
23
  const dirPath = dirname(filePath);
24
24
  if (!existsSync(dirPath)) {
@@ -26,7 +26,7 @@ export class SyncFileWriterService {
26
26
  }
27
27
  writeFileSync(filePath, generatedContent);
28
28
  }
29
- resolveOutputPath(outputDir, fileName = 'type.ts') {
29
+ resolveOutputPath(outputDir, fileName = 'api.ts') {
30
30
  return resolve(outputDir, fileName);
31
31
  }
32
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"import-builder.service.d.ts","sourceRoot":"","sources":["../../../src/services/import-builder.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,8BAA8B,CAAC;AAKhE,QAAA,MAAM,aAAa;;;iBAGjB,CAAC;AAEH,KAAK,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEvD,qBAAa,8BAA+B,YAAW,aAAa;IAClE,YAAY,IAAI,EAAE,CAAC,iBAAiB,EAAE;IAQtC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,EAAE,CAAC,iBAAiB;CAqC/E"}
1
+ {"version":3,"file":"import-builder.service.d.ts","sourceRoot":"","sources":["../../../src/services/import-builder.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAKlE,QAAA,MAAM,aAAa;;;iBAGjB,CAAC;AAEH,KAAK,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEvD,qBAAa,8BAA+B,YAAW,aAAa;IAClE,YAAY,IAAI,EAAE,CAAC,iBAAiB,EAAE;IAQtC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,EAAE,CAAC,iBAAiB;CAiC/E"}
@@ -4,14 +4,14 @@ const IsTypeImport = z.boolean();
4
4
  const ImportedElement = z.record(z.string(), IsTypeImport);
5
5
  const ImportOptions = z.object({
6
6
  defaultImport: ImportedElement.optional(),
7
- namedImports: ImportedElement.optional(),
7
+ namedImports: ImportedElement.optional()
8
8
  });
9
9
  export class TypeScriptImportBuilderService {
10
10
  buildImports() {
11
11
  return [
12
12
  this.createImport('zod', {
13
- namedImports: { z: false },
14
- }),
13
+ namedImports: { z: false }
14
+ })
15
15
  ];
16
16
  }
17
17
  createImport(target, options) {
@@ -1 +1 @@
1
- {"version":3,"file":"type-builder.service.d.ts","sourceRoot":"","sources":["../../../src/services/type-builder.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,8BAA8B,CAAC;AAE9D,qBAAa,4BAA6B,YAAW,WAAW;IAC9D,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC,QAAQ;IA6BpC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,UAAQ,GAAG,EAAE,CAAC,mBAAmB;IAYtF,eAAe,CACb,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,QAAQ,EAC3B,YAAY,CAAC,EAAE,EAAE,CAAC,UAAU,EAC5B,UAAU,UAAQ,GACjB,EAAE,CAAC,oBAAoB;IAW1B,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC,wBAAwB;IAS5D,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAMxC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIjC,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAGnC"}
1
+ {"version":3,"file":"type-builder.service.d.ts","sourceRoot":"","sources":["../../../src/services/type-builder.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAEhE,qBAAa,4BAA6B,YAAW,WAAW;IAC9D,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC,QAAQ;IA4BpC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,UAAQ,GAAG,EAAE,CAAC,mBAAmB;IAYtF,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,EAAE,CAAC,UAAU,EAAE,UAAU,UAAQ,GAAG,EAAE,CAAC,oBAAoB;IAWrI,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC,wBAAwB;IAI5D,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAMxC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIjC,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAGnC"}
@@ -1 +1 @@
1
- {"version":3,"file":"generator-options.d.ts","sourceRoot":"","sources":["../../../src/types/generator-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,gBAAgB,EAAE,wBAAwB,EAAC,MAAM,4BAA4B,CAAC;AAE3F;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;;;;;OAYG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAEpC;;;;;;;;;;;;;;;;;;OAkBG;IACH,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;CACrD"}
1
+ {"version":3,"file":"generator-options.d.ts","sourceRoot":"","sources":["../../../src/types/generator-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AAE7F;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;;;;;OAYG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAEpC;;;;;;;;;;;;;;;;;;OAkBG;IACH,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;CACrD"}
@@ -1 +1 @@
1
- {"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../../../src/types/openapi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAEtB,eAAO,MAAM,SAAS;;iBAEpB,CAAC;AA6CH,eAAO,MAAM,gBAAgB,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAKxD,CAAC;AAcF,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;iBAYpB,CAAC;AAkBH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;iBAKnB,CAAC;AAEH,eAAO,MAAM,WAAW;;;;;;;iBAKtB,CAAC;AAEH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBASvB,CAAC;AAEH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAanB,CAAC;AA+CH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAStB,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAC1D,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AACpE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AACtD,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAC;AACpD,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAC1D,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAC5D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAC;AACpD,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC"}
1
+ {"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../../../src/types/openapi.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,SAAS;;iBAEpB,CAAC;AA6CH,eAAO,MAAM,gBAAgB,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAKxD,CAAC;AAcF,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;iBAYpB,CAAC;AAkBH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;iBAKnB,CAAC;AAEH,eAAO,MAAM,WAAW;;;;;;;iBAKtB,CAAC;AAEH,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBASvB,CAAC;AAEH,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAanB,CAAC;AA+CH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAStB,CAAC;AAEH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAC1D,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AACpE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC;AACtD,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAC;AACpD,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAC1D,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAC5D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAC;AACpD,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,SAAS,CAAC,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  export const Reference = z.object({
3
- $ref: z.string().optional(),
3
+ $ref: z.string().optional()
4
4
  });
5
5
  const BaseSchemaProperties = z.object({
6
6
  $ref: z.string().optional(),
@@ -36,27 +36,27 @@ const BaseSchemaProperties = z.object({
36
36
  xml: z
37
37
  .object({
38
38
  name: z.string().optional(),
39
- wrapped: z.boolean().optional(),
39
+ wrapped: z.boolean().optional()
40
40
  })
41
41
  .optional(),
42
42
  externalDocs: Reference.optional(),
43
43
  example: z.unknown().optional(),
44
- deprecated: z.boolean().optional(),
44
+ deprecated: z.boolean().optional()
45
45
  });
46
46
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
47
  export const SchemaProperties = z.lazy(() => BaseSchemaProperties.extend({
48
48
  properties: z.record(z.string(), SchemaProperties).optional(),
49
- items: SchemaProperties.optional(),
49
+ items: SchemaProperties.optional()
50
50
  }));
51
51
  const ServerVariable = z.object({
52
52
  default: z.string(),
53
53
  description: z.string().optional(),
54
- enum: z.array(z.string()).optional(),
54
+ enum: z.array(z.string()).optional()
55
55
  });
56
56
  const Server = z.object({
57
57
  url: z.string(), // Allow templated URLs with {variables}
58
58
  description: z.string().optional(),
59
- variables: z.record(z.string(), ServerVariable).optional(),
59
+ variables: z.record(z.string(), ServerVariable).optional()
60
60
  });
61
61
  export const Parameter = z.object({
62
62
  $ref: z.string().optional(),
@@ -69,7 +69,7 @@ export const Parameter = z.object({
69
69
  style: z.string().optional(),
70
70
  explode: z.boolean().optional(),
71
71
  allowReserved: z.boolean().optional(),
72
- schema: SchemaProperties.optional(),
72
+ schema: SchemaProperties.optional()
73
73
  });
74
74
  const ResponseHeader = z.object({
75
75
  $ref: z.string().optional(),
@@ -80,22 +80,22 @@ const ResponseHeader = z.object({
80
80
  style: z.string().optional(),
81
81
  explode: z.boolean().optional(),
82
82
  allowReserved: z.boolean().optional(),
83
- schema: Reference.optional(),
83
+ schema: Reference.optional()
84
84
  });
85
85
  const MediaType = z.object({
86
- schema: z.unknown().optional(),
86
+ schema: z.unknown().optional()
87
87
  });
88
88
  export const Response = z.object({
89
89
  $ref: z.string().optional(),
90
90
  description: z.string(),
91
91
  headers: z.record(z.string(), ResponseHeader).optional(),
92
- content: z.record(z.string(), MediaType).optional(),
92
+ content: z.record(z.string(), MediaType).optional()
93
93
  });
94
94
  export const RequestBody = z.object({
95
95
  $ref: z.string().optional(),
96
96
  description: z.string().optional(),
97
97
  required: z.boolean().optional(),
98
- content: z.record(z.string(), MediaType).optional(),
98
+ content: z.record(z.string(), MediaType).optional()
99
99
  });
100
100
  export const MethodSchema = z.object({
101
101
  summary: z.string().optional(),
@@ -105,7 +105,7 @@ export const MethodSchema = z.object({
105
105
  requestBody: RequestBody.optional(),
106
106
  responses: z.record(z.string(), Response).optional(),
107
107
  tags: z.array(z.string()).optional(),
108
- deprecated: z.boolean().optional(),
108
+ deprecated: z.boolean().optional()
109
109
  });
110
110
  export const PathItem = z.object({
111
111
  $ref: z.string().optional(),
@@ -119,7 +119,7 @@ export const PathItem = z.object({
119
119
  head: MethodSchema.optional(),
120
120
  options: MethodSchema.optional(),
121
121
  trace: MethodSchema.optional(),
122
- parameters: z.array(Parameter).optional(),
122
+ parameters: z.array(Parameter).optional()
123
123
  });
124
124
  const Info = z.object({
125
125
  title: z.string().min(1),
@@ -130,25 +130,25 @@ const Info = z.object({
130
130
  .object({
131
131
  name: z.string().optional(),
132
132
  email: z.email().optional(),
133
- url: z.url().optional(),
133
+ url: z.url().optional()
134
134
  })
135
135
  .optional(),
136
136
  license: z
137
137
  .object({
138
138
  name: z.string().min(1),
139
- url: z.url().optional(),
139
+ url: z.url().optional()
140
140
  })
141
- .optional(),
141
+ .optional()
142
142
  });
143
143
  const SecurityRequirement = z.record(z.string(), z.array(z.string()));
144
144
  const Tag = z.object({
145
145
  name: z.string().min(1),
146
146
  description: z.string().optional(),
147
- externalDocs: Reference.optional(),
147
+ externalDocs: Reference.optional()
148
148
  });
149
149
  const ExternalDocumentation = z.object({
150
150
  description: z.string().optional(),
151
- url: z.url(),
151
+ url: z.url()
152
152
  });
153
153
  const Components = z.object({
154
154
  schemas: z.record(z.string(), SchemaProperties).optional(),
@@ -159,7 +159,7 @@ const Components = z.object({
159
159
  headers: z.record(z.string(), ResponseHeader).optional(),
160
160
  securitySchemes: z.record(z.string(), Reference).optional(),
161
161
  links: z.record(z.string(), Reference).optional(),
162
- callbacks: z.record(z.string(), Reference).optional(),
162
+ callbacks: z.record(z.string(), Reference).optional()
163
163
  });
164
164
  export const OpenApiSpec = z.object({
165
165
  openapi: z.string().regex(/^3\.\d+\.\d+$/, 'OpenAPI version must be in format 3.x.x'),
@@ -169,5 +169,5 @@ export const OpenApiSpec = z.object({
169
169
  components: Components.optional(),
170
170
  security: z.array(SecurityRequirement).optional(),
171
171
  tags: z.array(Tag).optional(),
172
- externalDocs: ExternalDocumentation.optional(),
172
+ externalDocs: ExternalDocumentation.optional()
173
173
  });
@@ -1 +1 @@
1
- {"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../../src/utils/error-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AAEzC,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,CAAC,OAAO,EAAE,WAAW,MAAM,EAAE,UAAU,QAAQ,WAAS,IAGpG,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,CAAC,OAAO,EAAE,WAAW,MAAM,EAAE,UAAU,QAAQ,KAAG,IAK7F,CAAC"}
1
+ {"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../../src/utils/error-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,CAAC,OAAO,EAAE,WAAW,MAAM,EAAE,UAAU,QAAQ,WAAS,IAGpG,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,CAAC,OAAO,EAAE,WAAW,MAAM,EAAE,UAAU,QAAQ,KAAG,IAK7F,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"naming-convention.d.ts","sourceRoot":"","sources":["../../../src/utils/naming-convention.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,MAAM,gBAAgB,GACxB,WAAW,GACX,YAAY,GACZ,YAAY,GACZ,YAAY,GACZ,sBAAsB,GACtB,sBAAsB,CAAC;AAE3B;;;;;;;;;;GAUG;AACH,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,EAAE,gBAAgB,KAAK,MAAM,CAAC;AA0G7E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,GAAG,MAAM,CAsB7F"}
1
+ {"version":3,"file":"naming-convention.d.ts","sourceRoot":"","sources":["../../../src/utils/naming-convention.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,YAAY,GAAG,YAAY,GAAG,YAAY,GAAG,sBAAsB,GAAG,sBAAsB,CAAC;AAE1I;;;;;;;;;;GAUG;AACH,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,+BAA+B;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,wBAAwB,GAAG,CAAC,OAAO,EAAE,gBAAgB,KAAK,MAAM,CAAC;AAmH7E;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,GAAG,MAAM,CAsB7F"}
@@ -2,8 +2,9 @@
2
2
  * Capitalizes the first letter of a word
3
3
  */
4
4
  function capitalize(word) {
5
- if (word.length === 0)
5
+ if (word.length === 0) {
6
6
  return word;
7
+ }
7
8
  return word.charAt(0).toUpperCase() + word.slice(1);
8
9
  }
9
10
  /**
@@ -52,11 +53,13 @@ function normalizeToWords(input) {
52
53
  * Converts words array to camelCase
53
54
  */
54
55
  function toCamelCase(words) {
55
- if (words.length === 0)
56
+ if (words.length === 0) {
56
57
  return '';
58
+ }
57
59
  const [first, ...rest] = words;
58
- if (!first)
60
+ if (!first) {
59
61
  return '';
62
+ }
60
63
  return first + rest.map((w) => capitalize(w)).join('');
61
64
  }
62
65
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"signal-handler.d.ts","sourceRoot":"","sources":["../../../src/utils/signal-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AAEzC,eAAO,MAAM,cAAc,GACxB,SAAS,MAAM,CAAC,OAAO,EAAE,WAAW,MAAM,EAAE,OAAO,MAAM,CAAC,OAAO,EAAE,UAAU,QAAQ,WAAS,IAI9F,CAAC;AAEJ,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,CAAC,OAAO,EAAE,WAAW,MAAM,EAAE,UAAU,QAAQ,KAAG,IAK9F,CAAC"}
1
+ {"version":3,"file":"signal-handler.d.ts","sourceRoot":"","sources":["../../../src/utils/signal-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,CAAC,OAAO,EAAE,WAAW,MAAM,EAAE,OAAO,MAAM,CAAC,OAAO,EAAE,UAAU,QAAQ,WAAS,IAI5H,CAAC;AAEF,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,CAAC,OAAO,EAAE,WAAW,MAAM,EAAE,UAAU,QAAQ,KAAG,IAK9F,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli-comprehensive.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli-comprehensive.test.d.ts","sourceRoot":"","sources":["../../../tests/integration/cli-comprehensive.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,110 @@
1
+ import { execSync } from 'node:child_process';
2
+ import { existsSync, readFileSync, rmSync } from 'node:fs';
3
+ import { resolve } from 'node:path';
4
+ import { describe, expect, it } from 'vitest';
5
+ describe('CLI Comprehensive Integration', () => {
6
+ const cwd = resolve(__dirname, '../..');
7
+ const testOutputDir = resolve(cwd, 'test-output-cli');
8
+ beforeEach(() => {
9
+ if (existsSync(testOutputDir)) {
10
+ rmSync(testOutputDir, { recursive: true, force: true });
11
+ }
12
+ });
13
+ afterEach(() => {
14
+ if (existsSync(testOutputDir)) {
15
+ rmSync(testOutputDir, { recursive: true, force: true });
16
+ }
17
+ });
18
+ describe('Basic CLI Usage', () => {
19
+ it('should generate code with default output directory', () => {
20
+ execSync('node ./dist/src/cli.js --input ./samples/swagger-petstore.yaml --output generated', {
21
+ encoding: 'utf-8',
22
+ cwd
23
+ });
24
+ const outputFile = resolve(cwd, 'generated/api.ts');
25
+ expect(existsSync(outputFile)).toBe(true);
26
+ const content = readFileSync(outputFile, 'utf-8');
27
+ expect(content).toContain('SwaggerPetstoreOpenAPI30');
28
+ });
29
+ it('should generate code with custom output directory', () => {
30
+ execSync(`node ./dist/src/cli.js --input ./samples/swagger-petstore.yaml --output ${testOutputDir}`, {
31
+ encoding: 'utf-8',
32
+ cwd
33
+ });
34
+ const outputFile = resolve(testOutputDir, 'api.ts');
35
+ expect(existsSync(outputFile)).toBe(true);
36
+ });
37
+ it('should accept naming convention option', () => {
38
+ execSync(`node ./dist/src/cli.js --input ./samples/swagger-petstore.yaml --output ${testOutputDir} --naming-convention camelCase`, {
39
+ encoding: 'utf-8',
40
+ cwd
41
+ });
42
+ const outputFile = resolve(testOutputDir, 'api.ts');
43
+ const content = readFileSync(outputFile, 'utf-8');
44
+ // Verify camelCase is applied (operation IDs should be camelCase)
45
+ expect(content).toMatch(/async \w+\(/);
46
+ });
47
+ it('should reject invalid naming convention', () => {
48
+ expect(() => {
49
+ execSync(`node ./dist/src/cli.js --input ./samples/swagger-petstore.yaml --output ${testOutputDir} --naming-convention invalid`, {
50
+ encoding: 'utf-8',
51
+ cwd,
52
+ stdio: 'pipe'
53
+ });
54
+ }).toThrow();
55
+ });
56
+ it('should write to custom file when output is a .ts path', () => {
57
+ const outputFile = resolve(testOutputDir, 'client.ts');
58
+ execSync(`node ./dist/src/cli.js --input ./samples/swagger-petstore.yaml --output "${outputFile}"`, {
59
+ encoding: 'utf-8',
60
+ cwd
61
+ });
62
+ expect(existsSync(outputFile)).toBe(true);
63
+ const content = readFileSync(outputFile, 'utf-8');
64
+ expect(content).toContain('SwaggerPetstoreOpenAPI30');
65
+ });
66
+ });
67
+ describe('CLI Error Handling', () => {
68
+ it('should exit with code 1 on invalid input file', () => {
69
+ try {
70
+ execSync('node ./dist/src/cli.js --input ./nonexistent.yaml --output generated', {
71
+ encoding: 'utf-8',
72
+ cwd,
73
+ stdio: 'pipe'
74
+ });
75
+ expect.fail('Should have thrown an error');
76
+ }
77
+ catch (error) {
78
+ // Command should fail
79
+ expect(error).toBeDefined();
80
+ }
81
+ });
82
+ it('should require input option', () => {
83
+ expect(() => {
84
+ execSync('node ./dist/src/cli.js --output generated', {
85
+ encoding: 'utf-8',
86
+ cwd,
87
+ stdio: 'pipe'
88
+ });
89
+ }).toThrow();
90
+ });
91
+ });
92
+ describe('CLI with Different Spec Formats', () => {
93
+ it('should handle JSON format', () => {
94
+ execSync(`node ./dist/src/cli.js --input ./samples/pokeapi-openapi.json --output ${testOutputDir}`, {
95
+ encoding: 'utf-8',
96
+ cwd
97
+ });
98
+ const outputFile = resolve(testOutputDir, 'api.ts');
99
+ expect(existsSync(outputFile)).toBe(true);
100
+ });
101
+ it('should handle YAML format', () => {
102
+ execSync(`node ./dist/src/cli.js --input ./samples/swagger-petstore.yaml --output ${testOutputDir}`, {
103
+ encoding: 'utf-8',
104
+ cwd
105
+ });
106
+ const outputFile = resolve(testOutputDir, 'api.ts');
107
+ expect(existsSync(outputFile)).toBe(true);
108
+ });
109
+ });
110
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=cli.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.test.d.ts","sourceRoot":"","sources":["../../../tests/integration/cli.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,25 @@
1
+ import { execSync } from 'node:child_process';
2
+ import { resolve } from 'node:path';
3
+ import { describe, expect, it } from 'vitest';
4
+ describe('CLI Integration', () => {
5
+ describe('--help', () => {
6
+ it('should display help information', () => {
7
+ const result = execSync('node ./dist/src/cli.js --help', {
8
+ encoding: 'utf-8',
9
+ cwd: resolve(__dirname, '../..')
10
+ });
11
+ expect(result).toContain('Usage:');
12
+ expect(result).toContain('--input');
13
+ expect(result).toContain('--output');
14
+ });
15
+ });
16
+ describe('--version', () => {
17
+ it('should display version information', () => {
18
+ const result = execSync('node ./dist/src/cli.js --version', {
19
+ encoding: 'utf-8',
20
+ cwd: resolve(__dirname, '../..')
21
+ });
22
+ expect(result).toMatch(/\d+\.\d+\.\d+/);
23
+ });
24
+ });
25
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=error-scenarios.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error-scenarios.test.d.ts","sourceRoot":"","sources":["../../../tests/integration/error-scenarios.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,169 @@
1
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
+ import { Generator } from '../../src/generator';
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+ const testOutputDir = join(__dirname, '../../test-output-errors');
8
+ describe('Error Scenarios', () => {
9
+ let generator;
10
+ let mockReporter;
11
+ const logSpy = vi.fn();
12
+ const errorSpy = vi.fn();
13
+ beforeEach(() => {
14
+ if (existsSync(testOutputDir)) {
15
+ rmSync(testOutputDir, { recursive: true, force: true });
16
+ }
17
+ mkdirSync(testOutputDir, { recursive: true });
18
+ mockReporter = {
19
+ log: logSpy,
20
+ error: errorSpy
21
+ };
22
+ logSpy.mockClear();
23
+ errorSpy.mockClear();
24
+ });
25
+ afterEach(() => {
26
+ if (existsSync(testOutputDir)) {
27
+ rmSync(testOutputDir, { recursive: true, force: true });
28
+ }
29
+ });
30
+ describe('File Reading Errors', () => {
31
+ it('should handle non-existent local file', async () => {
32
+ generator = new Generator('test-app', '1.0.0', mockReporter, './non-existent-file.yaml', testOutputDir);
33
+ const exitCode = await generator.run();
34
+ expect(exitCode).toBe(1);
35
+ expect(errorSpy).toHaveBeenCalled();
36
+ expect(String(errorSpy.mock.calls[0]?.[0] ?? '')).toContain('Error');
37
+ });
38
+ it('should handle network errors when fetching URL', async () => {
39
+ const originalFetch = global.fetch;
40
+ const mockFetch = vi.fn().mockRejectedValue(new Error('Network error'));
41
+ global.fetch = mockFetch;
42
+ generator = new Generator('test-app', '1.0.0', mockReporter, 'https://example.com/nonexistent.json', testOutputDir);
43
+ const exitCode = await generator.run();
44
+ expect(exitCode).toBe(1);
45
+ expect(errorSpy).toHaveBeenCalled();
46
+ expect(mockFetch).toHaveBeenCalled();
47
+ global.fetch = originalFetch;
48
+ });
49
+ it('should handle HTTP errors when fetching URL', async () => {
50
+ const originalFetch = global.fetch;
51
+ const mockFetch = vi.fn().mockResolvedValue({
52
+ ok: false,
53
+ status: 500,
54
+ statusText: 'Internal Server Error',
55
+ text: async () => 'Error response'
56
+ });
57
+ global.fetch = mockFetch;
58
+ generator = new Generator('test-app', '1.0.0', mockReporter, 'https://example.com/error.json', testOutputDir);
59
+ const exitCode = await generator.run();
60
+ expect(exitCode).toBe(1);
61
+ expect(errorSpy).toHaveBeenCalled();
62
+ // Error should mention the HTTP error or parsing failure
63
+ const errorMessage = (errorSpy.mock.calls[0]?.[0] ?? '');
64
+ expect(errorMessage).toMatch(/500|Error|Failed/);
65
+ global.fetch = originalFetch;
66
+ });
67
+ });
68
+ describe('Parsing Errors', () => {
69
+ it('should handle invalid JSON', async () => {
70
+ const invalidFile = join(testOutputDir, 'invalid.json');
71
+ writeFileSync(invalidFile, '{ invalid json }');
72
+ generator = new Generator('test-app', '1.0.0', mockReporter, invalidFile, testOutputDir);
73
+ const exitCode = await generator.run();
74
+ expect(exitCode).toBe(1);
75
+ expect(errorSpy).toHaveBeenCalled();
76
+ });
77
+ it('should handle invalid YAML', async () => {
78
+ const invalidFile = join(testOutputDir, 'invalid.yaml');
79
+ writeFileSync(invalidFile, 'invalid: yaml: [unclosed');
80
+ generator = new Generator('test-app', '1.0.0', mockReporter, invalidFile, testOutputDir);
81
+ const exitCode = await generator.run();
82
+ expect(exitCode).toBe(1);
83
+ expect(errorSpy).toHaveBeenCalled();
84
+ });
85
+ it('should handle invalid OpenAPI version', async () => {
86
+ const invalidFile = join(testOutputDir, 'invalid-version.yaml');
87
+ writeFileSync(invalidFile, `
88
+ openapi: 2.0.0
89
+ info:
90
+ title: Test API
91
+ version: 1.0.0
92
+ paths: {}
93
+ `);
94
+ generator = new Generator('test-app', '1.0.0', mockReporter, invalidFile, testOutputDir);
95
+ const exitCode = await generator.run();
96
+ expect(exitCode).toBe(1);
97
+ expect(errorSpy).toHaveBeenCalled();
98
+ });
99
+ it('should handle missing required fields', async () => {
100
+ const invalidFile = join(testOutputDir, 'missing-fields.yaml');
101
+ writeFileSync(invalidFile, `
102
+ openapi: 3.0.0
103
+ paths: {}
104
+ `);
105
+ generator = new Generator('test-app', '1.0.0', mockReporter, invalidFile, testOutputDir);
106
+ const exitCode = await generator.run();
107
+ expect(exitCode).toBe(1);
108
+ expect(errorSpy).toHaveBeenCalled();
109
+ });
110
+ });
111
+ describe('File Writing Errors', () => {
112
+ it('should handle write permission errors gracefully', async () => {
113
+ // Create a read-only directory (on Unix systems)
114
+ const readOnlyDir = join(testOutputDir, 'readonly');
115
+ mkdirSync(readOnlyDir, { recursive: true });
116
+ // Note: chmod doesn't work the same way on all systems, so this test
117
+ // might need to be adjusted based on the environment
118
+ generator = new Generator('test-app', '1.0.0', mockReporter, './samples/swagger-petstore.yaml', readOnlyDir);
119
+ const exitCode = await generator.run();
120
+ // Should either succeed or fail gracefully
121
+ expect([0, 1]).toContain(exitCode);
122
+ });
123
+ });
124
+ describe('Malformed OpenAPI Specs', () => {
125
+ it('should handle empty spec', async () => {
126
+ const emptyFile = join(testOutputDir, 'empty.yaml');
127
+ writeFileSync(emptyFile, '');
128
+ generator = new Generator('test-app', '1.0.0', mockReporter, emptyFile, testOutputDir);
129
+ const exitCode = await generator.run();
130
+ expect(exitCode).toBe(1);
131
+ expect(errorSpy).toHaveBeenCalled();
132
+ });
133
+ it('should handle spec with no paths', async () => {
134
+ const noPathsFile = join(testOutputDir, 'no-paths.yaml');
135
+ writeFileSync(noPathsFile, `
136
+ openapi: 3.0.0
137
+ info:
138
+ title: Test API
139
+ version: 1.0.0
140
+ paths: {}
141
+ `);
142
+ generator = new Generator('test-app', '1.0.0', mockReporter, noPathsFile, testOutputDir);
143
+ const exitCode = await generator.run();
144
+ // Should succeed even with no paths
145
+ expect(exitCode).toBe(0);
146
+ expect(logSpy).toHaveBeenCalled();
147
+ });
148
+ it('should handle spec with no components', async () => {
149
+ const noComponentsFile = join(testOutputDir, 'no-components.yaml');
150
+ writeFileSync(noComponentsFile, `
151
+ openapi: 3.0.0
152
+ info:
153
+ title: Test API
154
+ version: 1.0.0
155
+ paths:
156
+ /test:
157
+ get:
158
+ operationId: test
159
+ responses:
160
+ '200':
161
+ description: Success
162
+ `);
163
+ generator = new Generator('test-app', '1.0.0', mockReporter, noComponentsFile, testOutputDir);
164
+ const exitCode = await generator.run();
165
+ // Should succeed even with no components
166
+ expect(exitCode).toBe(0);
167
+ });
168
+ });
169
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=snapshots.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshots.test.d.ts","sourceRoot":"","sources":["../../../tests/integration/snapshots.test.ts"],"names":[],"mappings":""}