zod-codegen 1.6.3 → 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/.github/workflows/ci.yml +50 -48
  2. package/.github/workflows/release.yml +13 -3
  3. package/.husky/commit-msg +1 -1
  4. package/.husky/pre-commit +1 -1
  5. package/.lintstagedrc.json +5 -1
  6. package/.nvmrc +1 -1
  7. package/.prettierrc.json +12 -5
  8. package/CHANGELOG.md +17 -0
  9. package/CONTRIBUTING.md +12 -12
  10. package/EXAMPLES.md +135 -57
  11. package/PERFORMANCE.md +4 -4
  12. package/README.md +87 -64
  13. package/SECURITY.md +1 -1
  14. package/dist/src/cli.js +11 -18
  15. package/dist/src/generator.d.ts +2 -2
  16. package/dist/src/generator.d.ts.map +1 -1
  17. package/dist/src/generator.js +5 -3
  18. package/dist/src/interfaces/code-generator.d.ts.map +1 -1
  19. package/dist/src/services/code-generator.service.d.ts +3 -1
  20. package/dist/src/services/code-generator.service.d.ts.map +1 -1
  21. package/dist/src/services/code-generator.service.js +236 -219
  22. package/dist/src/services/file-reader.service.d.ts +2 -0
  23. package/dist/src/services/file-reader.service.d.ts.map +1 -1
  24. package/dist/src/services/file-reader.service.js +25 -11
  25. package/dist/src/services/file-writer.service.d.ts.map +1 -1
  26. package/dist/src/services/file-writer.service.js +2 -2
  27. package/dist/src/services/import-builder.service.d.ts.map +1 -1
  28. package/dist/src/services/import-builder.service.js +3 -3
  29. package/dist/src/services/type-builder.service.d.ts.map +1 -1
  30. package/dist/src/types/generator-options.d.ts.map +1 -1
  31. package/dist/src/types/openapi.d.ts.map +1 -1
  32. package/dist/src/types/openapi.js +20 -20
  33. package/dist/src/utils/error-handler.d.ts.map +1 -1
  34. package/dist/src/utils/naming-convention.d.ts.map +1 -1
  35. package/dist/src/utils/naming-convention.js +6 -3
  36. package/dist/src/utils/signal-handler.d.ts.map +1 -1
  37. package/dist/tests/integration/cli-comprehensive.test.d.ts +2 -0
  38. package/dist/tests/integration/cli-comprehensive.test.d.ts.map +1 -0
  39. package/dist/tests/integration/cli-comprehensive.test.js +123 -0
  40. package/dist/tests/integration/cli.test.d.ts +2 -0
  41. package/dist/tests/integration/cli.test.d.ts.map +1 -0
  42. package/dist/tests/integration/cli.test.js +25 -0
  43. package/dist/tests/integration/error-scenarios.test.d.ts +2 -0
  44. package/dist/tests/integration/error-scenarios.test.d.ts.map +1 -0
  45. package/dist/tests/integration/error-scenarios.test.js +169 -0
  46. package/dist/tests/integration/snapshots.test.d.ts +2 -0
  47. package/dist/tests/integration/snapshots.test.d.ts.map +1 -0
  48. package/dist/tests/integration/snapshots.test.js +100 -0
  49. package/dist/tests/unit/code-generator-edge-cases.test.d.ts +2 -0
  50. package/dist/tests/unit/code-generator-edge-cases.test.d.ts.map +1 -0
  51. package/dist/tests/unit/code-generator-edge-cases.test.js +506 -0
  52. package/dist/tests/unit/code-generator.test.d.ts +2 -0
  53. package/dist/tests/unit/code-generator.test.d.ts.map +1 -0
  54. package/dist/tests/unit/code-generator.test.js +1364 -0
  55. package/dist/tests/unit/file-reader.test.d.ts +2 -0
  56. package/dist/tests/unit/file-reader.test.d.ts.map +1 -0
  57. package/dist/tests/unit/file-reader.test.js +153 -0
  58. package/dist/tests/unit/generator.test.d.ts +2 -0
  59. package/dist/tests/unit/generator.test.d.ts.map +1 -0
  60. package/dist/tests/unit/generator.test.js +119 -0
  61. package/dist/tests/unit/naming-convention.test.d.ts +2 -0
  62. package/dist/tests/unit/naming-convention.test.d.ts.map +1 -0
  63. package/dist/tests/unit/naming-convention.test.js +256 -0
  64. package/dist/tests/unit/reporter.test.d.ts +2 -0
  65. package/dist/tests/unit/reporter.test.d.ts.map +1 -0
  66. package/dist/tests/unit/reporter.test.js +44 -0
  67. package/dist/tests/unit/type-builder.test.d.ts +2 -0
  68. package/dist/tests/unit/type-builder.test.d.ts.map +1 -0
  69. package/dist/tests/unit/type-builder.test.js +108 -0
  70. package/dist/vitest.config.d.ts.map +1 -1
  71. package/dist/vitest.config.js +10 -20
  72. package/eslint.config.mjs +38 -28
  73. package/examples/.gitkeep +1 -1
  74. package/examples/README.md +4 -2
  75. package/examples/petstore/README.md +18 -17
  76. package/examples/petstore/{type.ts → api.ts} +158 -74
  77. package/examples/petstore/authenticated-usage.ts +6 -4
  78. package/examples/petstore/basic-usage.ts +4 -3
  79. package/examples/petstore/error-handling-usage.ts +84 -0
  80. package/examples/petstore/retry-handler-usage.ts +11 -18
  81. package/examples/petstore/server-variables-usage.ts +10 -10
  82. package/examples/pokeapi/README.md +8 -8
  83. package/examples/pokeapi/api.ts +218 -0
  84. package/examples/pokeapi/basic-usage.ts +3 -2
  85. package/examples/pokeapi/custom-client.ts +5 -4
  86. package/package.json +17 -21
  87. package/src/cli.ts +20 -25
  88. package/src/generator.ts +13 -11
  89. package/src/interfaces/code-generator.ts +1 -1
  90. package/src/services/code-generator.service.ts +799 -1120
  91. package/src/services/file-reader.service.ts +35 -15
  92. package/src/services/file-writer.service.ts +7 -7
  93. package/src/services/import-builder.service.ts +9 -13
  94. package/src/services/type-builder.service.ts +8 -19
  95. package/src/types/generator-options.ts +1 -1
  96. package/src/types/openapi.ts +22 -22
  97. package/src/utils/error-handler.ts +2 -2
  98. package/src/utils/naming-convention.ts +13 -10
  99. package/src/utils/reporter.ts +2 -2
  100. package/src/utils/signal-handler.ts +7 -8
  101. package/tests/integration/cli-comprehensive.test.ts +53 -31
  102. package/tests/integration/cli.test.ts +5 -5
  103. package/tests/integration/error-scenarios.test.ts +20 -26
  104. package/tests/integration/snapshots.test.ts +19 -23
  105. package/tests/unit/code-generator-edge-cases.test.ts +133 -133
  106. package/tests/unit/code-generator.test.ts +431 -330
  107. package/tests/unit/file-reader.test.ts +58 -18
  108. package/tests/unit/generator.test.ts +30 -18
  109. package/tests/unit/naming-convention.test.ts +27 -27
  110. package/tests/unit/type-builder.test.ts +2 -2
  111. package/tsconfig.json +5 -3
  112. package/vitest.config.ts +11 -21
  113. package/dist/scripts/update-manifest.d.ts +0 -14
  114. package/dist/scripts/update-manifest.d.ts.map +0 -1
  115. package/dist/scripts/update-manifest.js +0 -33
  116. package/dist/src/assets/manifest.json +0 -5
  117. package/examples/pokeapi/type.ts +0 -109
  118. package/generated/type.ts +0 -371
  119. package/scripts/update-manifest.ts +0 -49
  120. package/src/assets/manifest.json +0 -5
package/README.md CHANGED
@@ -20,7 +20,7 @@ A powerful TypeScript code generator that creates **Zod schemas** and **type-saf
20
20
  - **🛡️ Runtime Validation**: Built-in Zod validation for request/response data
21
21
  - **🌍 Form Support**: Supports both JSON and form-urlencoded request bodies
22
22
  - **🔐 Extensible**: Override `getBaseRequestOptions()` to add authentication, custom headers, CORS, and other fetch options
23
- - **🔄 Response Handling**: Override `handleResponse()` to implement custom retry logic, logging, and error handling
23
+ - **🔄 Response Handling**: Override `handleResponse()` to implement custom retry logic, logging, error handling, and catch 4xx/5xx with custom errors
24
24
  - **🌐 Server Configuration**: Full support for OpenAPI server variables and templating (e.g., `{environment}.example.com`)
25
25
  - **⚙️ Flexible Client Options**: Options-based constructor supporting server selection, variable overrides, and custom base URLs
26
26
 
@@ -29,13 +29,13 @@ A powerful TypeScript code generator that creates **Zod schemas** and **type-saf
29
29
  ### Global Installation (CLI)
30
30
 
31
31
  ```bash
32
- yarn global add zod-codegen
32
+ npm install -g zod-codegen
33
33
  ```
34
34
 
35
35
  ### Project Installation
36
36
 
37
37
  ```bash
38
- yarn add --dev zod-codegen
38
+ npm install --save-dev zod-codegen
39
39
  ```
40
40
 
41
41
  ## 🔧 Usage
@@ -55,13 +55,13 @@ zod-codegen -i ./swagger.yaml -o ./src/generated
55
55
 
56
56
  #### CLI Options
57
57
 
58
- | Option | Alias | Description | Default |
59
- | --------------------- | ----- | ----------------------------------- | ----------- |
60
- | `--input` | `-i` | Path or URL to OpenAPI file | Required |
61
- | `--output` | `-o` | Directory to output generated files | `generated` |
62
- | `--naming-convention` | `-n` | Naming convention for operation IDs | (none) |
63
- | `--help` | `-h` | Show help | |
64
- | `--version` | `-v` | Show version | |
58
+ | Option | Alias | Description | Default |
59
+ | --------------------- | ----- | -------------------------------------------------------------- | ----------- |
60
+ | `--input` | `-i` | Path or URL to OpenAPI file | Required |
61
+ | `--output` | `-o` | Output directory (writes api.ts) or path to the generated file | `generated` |
62
+ | `--naming-convention` | `-n` | Naming convention for operation IDs | (none) |
63
+ | `--help` | `-h` | Show help | |
64
+ | `--version` | `-v` | Show version | |
65
65
 
66
66
  #### Naming Conventions
67
67
 
@@ -89,13 +89,13 @@ This is particularly useful when OpenAPI specs have inconsistent or poorly named
89
89
  ### Programmatic Usage
90
90
 
91
91
  ```typescript
92
- import {Generator} from 'zod-codegen';
93
- import type {GeneratorOptions} from 'zod-codegen';
92
+ import { Generator } from 'zod-codegen';
93
+ import type { GeneratorOptions } from 'zod-codegen';
94
94
 
95
95
  // Create a simple reporter object
96
96
  const reporter = {
97
97
  log: (...args: unknown[]) => console.log(...args),
98
- error: (...args: unknown[]) => console.error(...args),
98
+ error: (...args: unknown[]) => console.error(...args)
99
99
  };
100
100
 
101
101
  // Create generator instance with naming convention
@@ -106,8 +106,8 @@ const generator = new Generator(
106
106
  './openapi.json', // Input path or URL
107
107
  './generated', // Output directory
108
108
  {
109
- namingConvention: 'camelCase', // Transform operation IDs to camelCase
110
- },
109
+ namingConvention: 'camelCase' // Transform operation IDs to camelCase
110
+ }
111
111
  );
112
112
 
113
113
  // Run the generator
@@ -119,12 +119,12 @@ const exitCode = await generator.run();
119
119
  For more advanced use cases, you can provide a custom transformer function that receives full operation details:
120
120
 
121
121
  ```typescript
122
- import {Generator} from 'zod-codegen';
123
- import type {GeneratorOptions, OperationDetails} from 'zod-codegen';
122
+ import { Generator } from 'zod-codegen';
123
+ import type { GeneratorOptions, OperationDetails } from 'zod-codegen';
124
124
 
125
125
  const customTransformer: GeneratorOptions['operationNameTransformer'] = (details: OperationDetails) => {
126
126
  // details includes: operationId, method, path, tags, summary, description
127
- const {operationId, method, tags} = details;
127
+ const { operationId, method, tags } = details;
128
128
 
129
129
  // Example: Prefix with HTTP method and tag
130
130
  const tag = tags?.[0] || 'default';
@@ -132,7 +132,7 @@ const customTransformer: GeneratorOptions['operationNameTransformer'] = (details
132
132
  };
133
133
 
134
134
  const generator = new Generator('my-app', '1.0.0', reporter, './openapi.json', './generated', {
135
- operationNameTransformer: customTransformer,
135
+ operationNameTransformer: customTransformer
136
136
  });
137
137
  ```
138
138
 
@@ -140,10 +140,11 @@ const generator = new Generator('my-app', '1.0.0', reporter, './openapi.json', '
140
140
 
141
141
  ## 📁 Generated Output
142
142
 
143
- The generator creates a single TypeScript file (`type.ts`) containing:
143
+ The generator creates a single TypeScript file (`api.ts`) containing:
144
144
 
145
145
  - **Zod Schemas**: Exported Zod validation schemas for all component schemas defined in your OpenAPI spec
146
146
  - **API Client Class**: A type-safe client class with methods for each endpoint operation
147
+ - **ResponseValidationError**: A generic error class thrown when response data fails Zod schema validation, carrying the original response and error details
147
148
  - **Server Configuration**: `serverConfigurations` array and `defaultBaseUrl` constant extracted from OpenAPI servers
148
149
  - **Client Options Type**: `ClientOptions` type for flexible server selection and variable overrides
149
150
  - **Protected Extension Points**:
@@ -202,7 +203,7 @@ export const defaultBaseUrl: string; // First server with default variables
202
203
 
203
204
  ```
204
205
  generated/
205
- └── type.ts # All schemas and client in one file
206
+ └── api.ts # All schemas and client in one file
206
207
  ```
207
208
 
208
209
  ## 🎯 Example
@@ -247,23 +248,23 @@ components:
247
248
  required: [id, name, email]
248
249
  ```
249
250
 
250
- **Generated Output** (`generated/type.ts`):
251
+ **Generated Output** (`generated/api.ts`):
251
252
 
252
253
  ```typescript
253
- import {z} from 'zod';
254
+ import { z } from 'zod';
254
255
 
255
256
  // Components schemas
256
257
  export const User = z.object({
257
258
  id: z.number().int(),
258
259
  name: z.string(),
259
- email: z.string().email(),
260
+ email: z.string().email()
260
261
  });
261
262
 
262
263
  // Server configuration (when servers are defined in OpenAPI spec)
263
264
  export const serverConfigurations = [
264
265
  {
265
- url: 'https://api.example.com',
266
- },
266
+ url: 'https://api.example.com'
267
+ }
267
268
  ];
268
269
  export const defaultBaseUrl = 'https://api.example.com';
269
270
  export type ClientOptions = {
@@ -272,6 +273,21 @@ export type ClientOptions = {
272
273
  serverVariables?: Record<string, string>;
273
274
  };
274
275
 
276
+ // ResponseValidationError class
277
+ export class ResponseValidationError<T> extends Error {
278
+ readonly response: Response;
279
+ readonly error: z.ZodError<T>;
280
+ constructor(message: string, response: Response, error: z.ZodError<T>) {
281
+ super(message);
282
+ this.name = 'ResponseValidationError' as const;
283
+ this.response = response;
284
+ this.error = error;
285
+ }
286
+ get data(): T {
287
+ return this.response.json() as T;
288
+ }
289
+ }
290
+
275
291
  // Client class
276
292
  export class UserAPI {
277
293
  readonly #baseUrl: string;
@@ -284,18 +300,23 @@ export class UserAPI {
284
300
  return {};
285
301
  }
286
302
 
287
- async getUserById(id: number): Promise<z.infer<typeof User>> {
288
- return User.parse(await this.#makeRequest<z.infer<typeof User>>('GET', `/users/${id}`, {}));
303
+ async getUserById(id: number): Promise<User> {
304
+ const response = await this.makeRequest('GET', `/users/${id}`, {});
305
+ const parsedUser = User.safeParse(response);
306
+ if (!parsedUser.success) {
307
+ throw new ResponseValidationError<User>(`Invalid user: ...`, response, parsedUser.error);
308
+ }
309
+ return parsedUser.data;
289
310
  }
290
311
 
291
- // ... private #makeRequest method
312
+ // ... protected makeRequest method
292
313
  }
293
314
  ```
294
315
 
295
316
  **Usage:**
296
317
 
297
318
  ```typescript
298
- import {UserAPI, User} from './generated/type';
319
+ import UserAPI from './generated/api';
299
320
 
300
321
  // Use default server from OpenAPI spec
301
322
  const client = new UserAPI({});
@@ -316,7 +337,8 @@ The generated client includes a protected `getBaseRequestOptions()` method that
316
337
  #### Basic Authentication Example
317
338
 
318
339
  ```typescript
319
- import {UserAPI, ClientOptions} from './generated/type';
340
+ import UserAPI from './generated/api';
341
+ import type { ClientOptions } from './generated/api';
320
342
 
321
343
  class AuthenticatedUserAPI extends UserAPI {
322
344
  private accessToken: string | null = null;
@@ -331,8 +353,8 @@ class AuthenticatedUserAPI extends UserAPI {
331
353
  ...options,
332
354
  headers: {
333
355
  ...((options.headers as Record<string, string>) || {}),
334
- ...(this.accessToken ? {Authorization: `Bearer ${this.accessToken}`} : {}),
335
- },
356
+ ...(this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {})
357
+ }
336
358
  };
337
359
  }
338
360
 
@@ -350,7 +372,8 @@ const user = await client.getUserById(123); // Includes Authorization header
350
372
  #### Complete Configuration Example
351
373
 
352
374
  ```typescript
353
- import {UserAPI, ClientOptions} from './generated/type';
375
+ import UserAPI from './generated/api';
376
+ import type { ClientOptions } from './generated/api';
354
377
 
355
378
  class FullyConfiguredAPI extends UserAPI {
356
379
  private accessToken: string | null = null;
@@ -366,11 +389,11 @@ class FullyConfiguredAPI extends UserAPI {
366
389
  headers: {
367
390
  ...((options.headers as Record<string, string>) || {}),
368
391
  'User-Agent': 'MyApp/1.0.0',
369
- ...(this.accessToken ? {Authorization: `Bearer ${this.accessToken}`} : {}),
392
+ ...(this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {})
370
393
  },
371
394
  mode: 'cors',
372
395
  credentials: 'include',
373
- cache: 'no-cache',
396
+ cache: 'no-cache'
374
397
  };
375
398
  }
376
399
 
@@ -409,7 +432,7 @@ See [EXAMPLES.md](EXAMPLES.md) for comprehensive examples including:
409
432
  - CORS configuration
410
433
  - Request cancellation with AbortController
411
434
  - Environment-specific configurations
412
- - Response handling with custom retry logic and error handling
435
+ - Response handling with custom retry logic, [4xx/5xx error handling](EXAMPLES.md#handling-4xx5xx-in-handleresponse), and error transformation
413
436
 
414
437
  ## 📖 Examples
415
438
 
@@ -436,8 +459,8 @@ Each example includes:
436
459
 
437
460
  ### Prerequisites
438
461
 
439
- - Node.js ≥ 24.11.1
440
- - yarn
462
+ - Node.js ≥ 18.0.0 (see [.nvmrc](.nvmrc) for recommended version)
463
+ - npm
441
464
 
442
465
  ### Setup
443
466
 
@@ -447,56 +470,56 @@ git clone https://github.com/julienandreu/zod-codegen.git
447
470
  cd zod-codegen
448
471
 
449
472
  # Install dependencies
450
- yarn install
473
+ npm install
451
474
 
452
475
  # Build the project
453
- yarn build
476
+ npm run build
454
477
 
455
478
  # Run tests
456
- yarn test
479
+ npm test
457
480
 
458
481
  # Run linting
459
- yarn lint
482
+ npm run lint
460
483
 
461
484
  # Format code
462
- yarn format
485
+ npm run format
463
486
  ```
464
487
 
465
488
  ### Testing
466
489
 
467
490
  ```bash
468
491
  # Run all tests
469
- yarn test
492
+ npm test
470
493
 
471
494
  # Run tests in watch mode
472
- yarn test:watch
495
+ npm run test:watch
473
496
 
474
497
  # Run tests with coverage
475
- yarn test:coverage
498
+ npm run test:coverage
476
499
 
477
500
  # Run tests with UI
478
- yarn test:ui
501
+ npm run test:ui
479
502
  ```
480
503
 
481
504
  ### Available Scripts
482
505
 
483
- | Script | Description |
484
- | -------------------- | ----------------------------------------------- |
485
- | `yarn build` | Build the project |
486
- | `yarn build:watch` | Build in watch mode |
487
- | `yarn dev` | Development mode with example |
488
- | `yarn test` | Run tests |
489
- | `yarn test:watch` | Run tests in watch mode |
490
- | `yarn test:coverage` | Run tests with coverage |
491
- | `yarn lint` | Lint and fix code |
492
- | `yarn format` | Format code with Prettier |
493
- | `yarn type-check` | Type check without emitting |
494
- | `yarn validate` | Run all checks (lint, format, type-check, test) |
495
- | `yarn clean` | Clean build artifacts |
506
+ | Script | Description |
507
+ | ----------------------- | ----------------------------------------------- |
508
+ | `npm run build` | Build the project |
509
+ | `npm run build:watch` | Build in watch mode |
510
+ | `npm run dev` | Development mode with example |
511
+ | `npm test` | Run tests |
512
+ | `npm run test:watch` | Run tests in watch mode |
513
+ | `npm run test:coverage` | Run tests with coverage |
514
+ | `npm run lint` | Lint and fix code |
515
+ | `npm run format` | Format code with Prettier |
516
+ | `npm run type-check` | Type check without emitting |
517
+ | `npm run validate` | Run all checks (lint, format, type-check, test) |
518
+ | `npm run clean` | Clean build artifacts |
496
519
 
497
520
  ## 📋 Requirements
498
521
 
499
- - **Node.js**: ≥ 24.11.1
522
+ - **Node.js**: ≥ 18.0.0
500
523
  - **TypeScript**: ≥ 5.9.3
501
524
  - **Zod**: ≥ 4.1.12
502
525
 
@@ -509,8 +532,8 @@ We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) f
509
532
  1. Fork the repository
510
533
  2. Create your feature branch: `git checkout -b feature/amazing-feature`
511
534
  3. Make your changes
512
- 4. Run tests: `yarn test`
513
- 5. Run validation: `yarn validate`
535
+ 4. Run tests: `npm test`
536
+ 5. Run validation: `npm run validate`
514
537
  6. Commit your changes: `git commit -m 'feat: add amazing feature'`
515
538
  7. Push to the branch: `git push origin feature/amazing-feature`
516
539
  8. Open a Pull Request
package/SECURITY.md CHANGED
@@ -75,7 +75,7 @@ When using zod-codegen, please follow these security best practices:
75
75
 
76
76
  - Keep zod-codegen and its dependencies up to date
77
77
  - Regularly audit your dependency tree for known vulnerabilities
78
- - Use tools like `yarn audit` or `npm audit` to check for security issues
78
+ - Use `npm audit` to check for security issues
79
79
 
80
80
  ## Known Security Considerations
81
81
 
package/dist/src/cli.js CHANGED
@@ -1,15 +1,15 @@
1
1
  #!/usr/bin/env node
2
+ import { readFileSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
2
5
  import yargs from 'yargs';
3
6
  import { hideBin } from 'yargs/helpers';
4
7
  import { Generator } from './generator.js';
5
- import { readFileSync } from 'node:fs';
6
- import { fileURLToPath } from 'node:url';
7
- import { dirname, join } from 'node:path';
8
+ import debug from 'debug';
8
9
  import loudRejection from 'loud-rejection';
9
10
  import { handleErrors } from './utils/error-handler.js';
10
- import { handleSignals } from './utils/signal-handler.js';
11
- import debug from 'debug';
12
11
  import { Reporter } from './utils/reporter.js';
12
+ import { handleSignals } from './utils/signal-handler.js';
13
13
  const __dirname = dirname(fileURLToPath(import.meta.url));
14
14
  // Read package.json from the project root
15
15
  // Handle multiple scenarios:
@@ -19,7 +19,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
19
19
  // Try multiple paths to ensure we find package.json
20
20
  const possiblePaths = [
21
21
  join(__dirname, '..', '..', 'package.json'), // dist/src/cli.js or node_modules/pkg/dist/src/cli.js
22
- join(__dirname, '..', 'package.json'), // src/cli.ts
22
+ join(__dirname, '..', 'package.json') // src/cli.ts
23
23
  ];
24
24
  let packageJsonPath;
25
25
  for (const path of possiblePaths) {
@@ -51,20 +51,20 @@ const argv = yargs(hideBin(process.argv))
51
51
  alias: 'i',
52
52
  type: 'string',
53
53
  description: 'Path or URL to OpenAPI file',
54
- demandOption: true,
54
+ demandOption: true
55
55
  })
56
56
  .option('output', {
57
57
  alias: 'o',
58
58
  type: 'string',
59
- description: 'Directory to output the generated files',
60
- default: 'generated',
59
+ description: 'Output directory (writes to <output>/api.ts) or path to the generated file (e.g. ./dist/api.ts). Default: generated',
60
+ default: 'api.ts'
61
61
  })
62
62
  .option('naming-convention', {
63
63
  alias: 'n',
64
64
  type: 'string',
65
65
  description: 'Naming convention to apply to operation IDs',
66
66
  choices: ['camelCase', 'PascalCase', 'snake_case', 'kebab-case', 'SCREAMING_SNAKE_CASE', 'SCREAMING-KEBAB-CASE'],
67
- default: undefined,
67
+ default: undefined
68
68
  })
69
69
  .strict()
70
70
  .help()
@@ -81,14 +81,7 @@ function isValidNamingConvention(value) {
81
81
  if (value === undefined) {
82
82
  return false;
83
83
  }
84
- const validConventions = [
85
- 'camelCase',
86
- 'PascalCase',
87
- 'snake_case',
88
- 'kebab-case',
89
- 'SCREAMING_SNAKE_CASE',
90
- 'SCREAMING-KEBAB-CASE',
91
- ];
84
+ const validConventions = ['camelCase', 'PascalCase', 'snake_case', 'kebab-case', 'SCREAMING_SNAKE_CASE', 'SCREAMING-KEBAB-CASE'];
92
85
  return validConventions.includes(value);
93
86
  }
94
87
  void (async () => {
@@ -1,5 +1,5 @@
1
- import type { Reporter } from './utils/reporter';
2
1
  import type { GeneratorOptions } from './types/generator-options';
2
+ import type { Reporter } from './utils/reporter';
3
3
  export type { GeneratorOptions } from './types/generator-options';
4
4
  export type { NamingConvention, OperationDetails, OperationNameTransformer } from './utils/naming-convention';
5
5
  /**
@@ -47,7 +47,7 @@ export declare class Generator {
47
47
  * @param _version - The version of the application/library (used in generated file headers)
48
48
  * @param reporter - Reporter instance for logging messages and errors
49
49
  * @param inputPath - Path or URL to the OpenAPI specification file
50
- * @param _outputDir - Directory where generated files will be written
50
+ * @param _outputDir - Output directory (writes to <dir>/api.ts) or path to the generated file (e.g. ./dist/api.ts)
51
51
  * @param options - Optional configuration for code generation
52
52
  */
53
53
  constructor(_name: string, _version: string, reporter: Reporter, inputPath: string, _outputDir: string, options?: GeneratorOptions);
@@ -1 +1 @@
1
- {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAMhE,YAAY,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAChE,YAAY,EAAC,gBAAgB,EAAE,gBAAgB,EAAE,wBAAwB,EAAC,MAAM,2BAA2B,CAAC;AAE5G;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,SAAS;IAkBlB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,UAAU;IArB7B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+B;IAC1D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;IAC7D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiC;IAC/D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAwB;IACnD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC;;;;;;;;;OASG;gBAEgB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EACnC,OAAO,GAAE,gBAAqB;IAOhC;;;;OAIG;IACG,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;YAqBd,QAAQ;IAItB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,SAAS;CAGlB"}
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/generator.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGjD,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAE9G;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,SAAS;IAkBlB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,UAAU;IArB7B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+B;IAC1D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;IAC7D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiC;IAC/D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAwB;IACnD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC;;;;;;;;;OASG;gBAEgB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EACnC,OAAO,GAAE,gBAAqB;IAQhC;;;;OAIG;IACG,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;YAqBd,QAAQ;IAItB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,SAAS;CAGlB"}
@@ -1,5 +1,6 @@
1
- import { OpenApiFileParserService, SyncFileReaderService } from './services/file-reader.service.js';
1
+ import { extname, resolve } from 'node:path';
2
2
  import { TypeScriptCodeGeneratorService } from './services/code-generator.service.js';
3
+ import { OpenApiFileParserService, SyncFileReaderService } from './services/file-reader.service.js';
3
4
  import { SyncFileWriterService } from './services/file-writer.service.js';
4
5
  /**
5
6
  * Main generator class for creating TypeScript code from OpenAPI specifications.
@@ -46,7 +47,7 @@ export class Generator {
46
47
  * @param _version - The version of the application/library (used in generated file headers)
47
48
  * @param reporter - Reporter instance for logging messages and errors
48
49
  * @param inputPath - Path or URL to the OpenAPI specification file
49
- * @param _outputDir - Directory where generated files will be written
50
+ * @param _outputDir - Output directory (writes to <dir>/api.ts) or path to the generated file (e.g. ./dist/api.ts)
50
51
  * @param options - Optional configuration for code generation
51
52
  */
52
53
  constructor(_name, _version, reporter, inputPath, _outputDir, options = {}) {
@@ -56,7 +57,8 @@ export class Generator {
56
57
  this.inputPath = inputPath;
57
58
  this._outputDir = _outputDir;
58
59
  this.fileWriter = new SyncFileWriterService(this._name, this._version, inputPath);
59
- this.outputPath = this.fileWriter.resolveOutputPath(this._outputDir);
60
+ const ext = extname(this._outputDir);
61
+ this.outputPath = ext === '.ts' || ext === '.tsx' ? resolve(this._outputDir) : this.fileWriter.resolveOutputPath(this._outputDir);
60
62
  this.codeGenerator = new TypeScriptCodeGeneratorService(options);
61
63
  }
62
64
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"code-generator.d.ts","sourceRoot":"","sources":["../../../src/interfaces/code-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAEtD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAAC;CACzC;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;CAC3D;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,IAAI,OAAO,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC;CAC5C;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACpE"}
1
+ {"version":3,"file":"code-generator.d.ts","sourceRoot":"","sources":["../../../src/interfaces/code-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAAC;CACzC;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;CAC3D;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,IAAI,OAAO,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC;CAC5C;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACpE"}
@@ -1,7 +1,7 @@
1
1
  import * as ts from 'typescript';
2
2
  import type { CodeGenerator, SchemaBuilder } from '../interfaces/code-generator';
3
- import type { OpenApiSpecType } from '../types/openapi';
4
3
  import type { GeneratorOptions } from '../types/generator-options';
4
+ import type { OpenApiSpecType } from '../types/openapi';
5
5
  export declare class TypeScriptCodeGeneratorService implements CodeGenerator, SchemaBuilder {
6
6
  private readonly typeBuilder;
7
7
  private readonly importBuilder;
@@ -12,6 +12,7 @@ export declare class TypeScriptCodeGeneratorService implements CodeGenerator, Sc
12
12
  private currentSchemaName;
13
13
  constructor(options?: GeneratorOptions);
14
14
  private readonly ZodAST;
15
+ private extractSchemaReferences;
15
16
  generate(spec: OpenApiSpecType): string;
16
17
  buildSchema(schema: unknown, required?: boolean): ts.CallExpression | ts.Identifier;
17
18
  private buildAST;
@@ -38,6 +39,7 @@ export declare class TypeScriptCodeGeneratorService implements CodeGenerator, Sc
38
39
  * Builds a TypeScript interface declaration for an object schema.
39
40
  */
40
41
  private buildInterfaceDeclaration;
42
+ private buildResponseValidationErrorClass;
41
43
  private buildClientClass;
42
44
  private buildConstructor;
43
45
  private buildGetBaseRequestOptionsMethod;
@@ -1 +1 @@
1
- {"version":3,"file":"code-generator.service.d.ts","sourceRoot":"","sources":["../../../src/services/code-generator.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC,OAAO,KAAK,EAAC,aAAa,EAAE,aAAa,EAAC,MAAM,8BAA8B,CAAC;AAC/E,OAAO,KAAK,EAAmB,eAAe,EAAgB,MAAM,kBAAkB,CAAC;AACvF,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,4BAA4B,CAAC;AAWjE,qBAAa,8BAA+B,YAAW,aAAa,EAAE,aAAa;IACjF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsC;IAClE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwC;IACtE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwD;IAChF,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA+B;IAChE,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAuC;IAGhF,OAAO,CAAC,eAAe,CAAqB;IAC5C,OAAO,CAAC,iBAAiB,CAAuB;gBAEpC,OAAO,GAAE,gBAAqB;IAK1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAGpB;IAEH,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM;IAMvC,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,UAAO,GAAG,EAAE,CAAC,cAAc,GAAG,EAAE,CAAC,UAAU;IA2BhF,OAAO,CAAC,QAAQ;IAuBhB,OAAO,CAAC,YAAY;IAmDpB,OAAO,CAAC,sBAAsB;IAK9B;;;OAGG;IACH,OAAO,CAAC,6BAA6B;IAkDrC;;OAEG;IACH,OAAO,CAAC,aAAa;IA2BrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA2EzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAgB9B;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAyBjC,OAAO,CAAC,gBAAgB;IAuBxB,OAAO,CAAC,gBAAgB;IAmGxB,OAAO,CAAC,gCAAgC;IAwBxC,OAAO,CAAC,yBAAyB;IAuBjC,OAAO,CAAC,sBAAsB;IA8pB9B,OAAO,CAAC,kBAAkB;IAgE1B;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAoC9B,OAAO,CAAC,mBAAmB;IAgH3B,OAAO,CAAC,mBAAmB;IA6D3B,OAAO,CAAC,qBAAqB;IAwF7B,OAAO,CAAC,gBAAgB;IA0CxB,OAAO,CAAC,iBAAiB;IAmCzB,OAAO,CAAC,iBAAiB;IA8BzB,OAAO,CAAC,eAAe;IAuDvB,OAAO,CAAC,wBAAwB;IA4IhC,OAAO,CAAC,gBAAgB;IAqBxB,OAAO,CAAC,6BAA6B;IAkPrC,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,aAAa;IAMrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAuFzB,OAAO,CAAC,WAAW;IA0CnB,OAAO,CAAC,aAAa;IAmjBrB,OAAO,CAAC,iBAAiB;IAuCzB,OAAO,CAAC,qBAAqB;IAe7B,OAAO,CAAC,oBAAoB;IAyE5B,OAAO,CAAC,8BAA8B;IActC,OAAO,CAAC,wBAAwB;IAyBhC,OAAO,CAAC,yBAAyB;IA8BjC,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,kBAAkB;IA8B1B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAmB3B;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;IAiFlC,OAAO,CAAC,eAAe;CAuCxB"}
1
+ {"version":3,"file":"code-generator.service.d.ts","sourceRoot":"","sources":["../../../src/services/code-generator.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAoB,eAAe,EAAiB,MAAM,kBAAkB,CAAC;AAMzF,qBAAa,8BAA+B,YAAW,aAAa,EAAE,aAAa;IACjF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsC;IAClE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwC;IACtE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0D;IAClF,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA+B;IAChE,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAuC;IAGhF,OAAO,CAAC,eAAe,CAAqB;IAC5C,OAAO,CAAC,iBAAiB,CAAuB;gBAEpC,OAAO,GAAE,gBAAqB;IAK1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAGpB;IAEH,OAAO,CAAC,uBAAuB;IA6B/B,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM;IAMvC,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,UAAO,GAAG,EAAE,CAAC,cAAc,GAAG,EAAE,CAAC,UAAU;IA2BhF,OAAO,CAAC,QAAQ;IAwBhB,OAAO,CAAC,YAAY;IA6CpB,OAAO,CAAC,sBAAsB;IAK9B;;;OAGG;IACH,OAAO,CAAC,6BAA6B;IA+CrC;;OAEG;IACH,OAAO,CAAC,aAAa;IA2BrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAsEzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAgB9B;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAmBjC,OAAO,CAAC,iCAAiC;IA4FzC,OAAO,CAAC,gBAAgB;IAoBxB,OAAO,CAAC,gBAAgB;IA8ExB,OAAO,CAAC,gCAAgC;IAqBxC,OAAO,CAAC,yBAAyB;IAkBjC,OAAO,CAAC,sBAAsB;IAkgB9B,OAAO,CAAC,kBAAkB;IA6D1B;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAoC9B,OAAO,CAAC,mBAAmB;IAuK3B,OAAO,CAAC,mBAAmB;IAoD3B,OAAO,CAAC,qBAAqB;IAwF7B,OAAO,CAAC,gBAAgB;IA0CxB,OAAO,CAAC,iBAAiB;IAmCzB,OAAO,CAAC,iBAAiB;IA2BzB,OAAO,CAAC,eAAe;IA6CvB,OAAO,CAAC,wBAAwB;IAqHhC,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,6BAA6B;IAkMrC,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,aAAa;IAMrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA6EzB,OAAO,CAAC,WAAW;IAiCnB,OAAO,CAAC,aAAa;IAgbrB,OAAO,CAAC,iBAAiB;IA6CzB,OAAO,CAAC,qBAAqB;IAO7B,OAAO,CAAC,oBAAoB;IAyD5B,OAAO,CAAC,8BAA8B;IActC,OAAO,CAAC,wBAAwB;IAyBhC,OAAO,CAAC,yBAAyB;IA2BjC,OAAO,CAAC,wBAAwB;IAahC,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,kBAAkB;IAuB1B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAe3B;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;IA6ElC,OAAO,CAAC,eAAe;CAqCxB"}