zod-codegen 1.5.1 → 1.6.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 +7 -1
- package/.github/workflows/release.yml +3 -3
- package/CHANGELOG.md +34 -0
- package/CONTRIBUTING.md +1 -1
- package/EXAMPLES.md +91 -12
- package/README.md +11 -4
- package/dist/scripts/add-js-extensions.d.ts +2 -0
- package/dist/scripts/add-js-extensions.d.ts.map +1 -0
- package/dist/scripts/add-js-extensions.js +66 -0
- package/dist/scripts/update-manifest.d.ts +14 -0
- package/dist/scripts/update-manifest.d.ts.map +1 -0
- package/dist/scripts/update-manifest.js +33 -0
- package/dist/src/assets/manifest.json +1 -1
- package/dist/src/cli.js +3 -3
- package/dist/src/generator.d.ts +46 -4
- package/dist/src/generator.d.ts.map +1 -1
- package/dist/src/generator.js +43 -1
- package/dist/src/interfaces/code-generator.d.ts +1 -1
- package/dist/src/interfaces/code-generator.d.ts.map +1 -1
- package/dist/src/services/code-generator.service.d.ts +5 -3
- package/dist/src/services/code-generator.service.d.ts.map +1 -1
- package/dist/src/services/code-generator.service.js +86 -20
- package/dist/src/services/file-reader.service.d.ts +2 -2
- package/dist/src/services/file-reader.service.d.ts.map +1 -1
- package/dist/src/services/file-writer.service.d.ts +1 -1
- package/dist/src/services/file-writer.service.d.ts.map +1 -1
- package/dist/src/services/import-builder.service.d.ts +1 -1
- package/dist/src/services/import-builder.service.d.ts.map +1 -1
- package/dist/src/services/type-builder.service.d.ts +1 -1
- package/dist/src/services/type-builder.service.d.ts.map +1 -1
- package/dist/src/types/generator-options.d.ts +1 -1
- package/dist/src/types/generator-options.d.ts.map +1 -1
- package/dist/src/utils/error-handler.d.ts +3 -2
- package/dist/src/utils/error-handler.d.ts.map +1 -1
- package/dist/src/utils/error-handler.js +4 -4
- package/dist/src/utils/reporter.d.ts +3 -2
- package/dist/src/utils/reporter.d.ts.map +1 -1
- package/dist/src/utils/reporter.js +7 -5
- package/dist/src/utils/signal-handler.d.ts +3 -2
- package/dist/src/utils/signal-handler.d.ts.map +1 -1
- package/dist/src/utils/signal-handler.js +4 -4
- package/examples/README.md +10 -1
- package/examples/petstore/README.md +6 -6
- package/examples/petstore/authenticated-usage.ts +1 -1
- package/examples/petstore/basic-usage.ts +1 -1
- package/examples/petstore/retry-handler-usage.ts +173 -0
- package/examples/petstore/server-variables-usage.ts +1 -1
- package/examples/petstore/type.ts +76 -53
- package/examples/pokeapi/README.md +3 -3
- package/examples/pokeapi/basic-usage.ts +1 -1
- package/examples/pokeapi/custom-client.ts +1 -1
- package/generated/type.ts +326 -0
- package/package.json +14 -19
- package/scripts/add-js-extensions.ts +79 -0
- package/scripts/update-manifest.ts +4 -2
- package/src/assets/manifest.json +1 -1
- package/src/cli.ts +7 -7
- package/src/generator.ts +51 -9
- package/src/interfaces/code-generator.ts +1 -1
- package/src/services/code-generator.service.ts +256 -122
- package/src/services/file-reader.service.ts +3 -3
- package/src/services/file-writer.service.ts +1 -1
- package/src/services/import-builder.service.ts +1 -1
- package/src/services/type-builder.service.ts +1 -1
- package/src/types/generator-options.ts +1 -1
- package/src/utils/error-handler.ts +6 -5
- package/src/utils/reporter.ts +6 -3
- package/src/utils/signal-handler.ts +10 -8
- package/tests/integration/cli-comprehensive.test.ts +123 -0
- package/tests/integration/cli.test.ts +2 -2
- package/tests/integration/error-scenarios.test.ts +240 -0
- package/tests/integration/snapshots.test.ts +131 -0
- package/tests/unit/code-generator-edge-cases.test.ts +551 -0
- package/tests/unit/code-generator.test.ts +385 -2
- package/tests/unit/file-reader.test.ts +16 -1
- package/tests/unit/generator.test.ts +19 -2
- package/tests/unit/naming-convention.test.ts +30 -1
- package/tests/unit/reporter.test.ts +63 -0
- package/tests/unit/type-builder.test.ts +131 -0
- package/tsconfig.json +3 -3
- package/dist/src/http/fetch-client.d.ts +0 -15
- package/dist/src/http/fetch-client.d.ts.map +0 -1
- package/dist/src/http/fetch-client.js +0 -140
- package/dist/src/polyfills/fetch.d.ts +0 -5
- package/dist/src/polyfills/fetch.d.ts.map +0 -1
- package/dist/src/polyfills/fetch.js +0 -18
- package/dist/src/types/http.d.ts +0 -25
- package/dist/src/types/http.d.ts.map +0 -1
- package/dist/src/types/http.js +0 -10
- package/dist/src/utils/manifest.d.ts +0 -8
- package/dist/src/utils/manifest.d.ts.map +0 -1
- package/dist/src/utils/manifest.js +0 -9
- package/dist/src/utils/tty.d.ts +0 -2
- package/dist/src/utils/tty.d.ts.map +0 -1
- package/dist/src/utils/tty.js +0 -3
- package/src/http/fetch-client.ts +0 -181
- package/src/polyfills/fetch.ts +0 -26
- package/src/types/http.ts +0 -35
- package/src/utils/manifest.ts +0 -17
- package/src/utils/tty.ts +0 -3
package/.github/workflows/ci.yml
CHANGED
|
@@ -76,7 +76,7 @@ jobs:
|
|
|
76
76
|
run: npm ci
|
|
77
77
|
|
|
78
78
|
- name: Run security audit
|
|
79
|
-
run: npm audit --audit-level=high
|
|
79
|
+
run: npm audit --audit-level=high --omit=dev
|
|
80
80
|
|
|
81
81
|
build:
|
|
82
82
|
name: Build & Package
|
|
@@ -137,6 +137,12 @@ jobs:
|
|
|
137
137
|
- name: Build project
|
|
138
138
|
run: npm run build
|
|
139
139
|
|
|
140
|
+
- name: Verify build output
|
|
141
|
+
run: |
|
|
142
|
+
test -f dist/src/cli.js || (echo "ERROR: dist/src/cli.js not found" && exit 1)
|
|
143
|
+
test -f dist/src/utils/reporter.js || (echo "ERROR: dist/src/utils/reporter.js not found" && exit 1)
|
|
144
|
+
echo "Build output verified successfully"
|
|
145
|
+
|
|
140
146
|
- name: Preview semantic-release
|
|
141
147
|
env:
|
|
142
148
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -44,12 +44,12 @@ jobs:
|
|
|
44
44
|
- name: Run formatter check
|
|
45
45
|
run: npm run format:check
|
|
46
46
|
|
|
47
|
-
- name: Run tests with coverage
|
|
48
|
-
run: npm run test:coverage
|
|
49
|
-
|
|
50
47
|
- name: Build project
|
|
51
48
|
run: npm run build
|
|
52
49
|
|
|
50
|
+
- name: Run tests with coverage
|
|
51
|
+
run: npm run test:coverage
|
|
52
|
+
|
|
53
53
|
- name: Test CLI functionality
|
|
54
54
|
run: |
|
|
55
55
|
echo '{"openapi": "3.0.0", "info": {"title": "Test", "version": "1.0.0"}, "paths": {}}' > test-openapi.json
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,37 @@
|
|
|
1
|
+
## <small>1.6.2 (2026-01-19)</small>
|
|
2
|
+
|
|
3
|
+
- fix: improve URL construction and filter undefined query params (#58) ([b9c3d32](https://github.com/julienandreu/zod-codegen/commit/b9c3d32)), closes [#58](https://github.com/julienandreu/zod-codegen/issues/58)
|
|
4
|
+
- chore(deps): bump the dev-dependencies group across 1 directory with 5 updates (#57) ([30110fd](https://github.com/julienandreu/zod-codegen/commit/30110fd)), closes [#57](https://github.com/julienandreu/zod-codegen/issues/57)
|
|
5
|
+
|
|
6
|
+
## <small>1.6.1 (2026-01-09)</small>
|
|
7
|
+
|
|
8
|
+
- Merge pull request #55 from julienandreu/fix/integration-tests-timeout ([1e48796](https://github.com/julienandreu/zod-codegen/commit/1e48796)), closes [#55](https://github.com/julienandreu/zod-codegen/issues/55)
|
|
9
|
+
- fix: remove npm build from integration tests to prevent timeouts ([1cfceac](https://github.com/julienandreu/zod-codegen/commit/1cfceac))
|
|
10
|
+
|
|
11
|
+
## 1.6.0 (2026-01-09)
|
|
12
|
+
|
|
13
|
+
- Merge pull request #54 from julienandreu/feat/response-handling-policies ([3f99c8f](https://github.com/julienandreu/zod-codegen/commit/3f99c8f)), closes [#54](https://github.com/julienandreu/zod-codegen/issues/54)
|
|
14
|
+
- fix: add path mapping for zod-codegen/policies in examples ([d3dd5c9](https://github.com/julienandreu/zod-codegen/commit/d3dd5c9))
|
|
15
|
+
- fix: export policies from package.json ([c1a3344](https://github.com/julienandreu/zod-codegen/commit/c1a3344))
|
|
16
|
+
- fix: prevent duplicate method names for HEAD/OPTIONS with same operationId ([8c0b891](https://github.com/julienandreu/zod-codegen/commit/8c0b891))
|
|
17
|
+
- fix: remove invalid semver-diff override causing CI failure ([278db05](https://github.com/julienandreu/zod-codegen/commit/278db05))
|
|
18
|
+
- fix: remove unnecessary Promise.resolve in error handler ([d730ef4](https://github.com/julienandreu/zod-codegen/commit/d730ef4))
|
|
19
|
+
- fix: update example to use zod-codegen/policies import path ([c7e3b07](https://github.com/julienandreu/zod-codegen/commit/c7e3b07))
|
|
20
|
+
- fix: update TypeScript module resolution for Node.js ESM compatibility ([2d8f6a0](https://github.com/julienandreu/zod-codegen/commit/2d8f6a0))
|
|
21
|
+
- fix: use extensionless imports in TypeScript source files ([dc0698c](https://github.com/julienandreu/zod-codegen/commit/dc0698c))
|
|
22
|
+
- ci: add build output verification step to diagnose module resolution issue ([5174320](https://github.com/julienandreu/zod-codegen/commit/5174320))
|
|
23
|
+
- docs: fix examples documentation ([04d6db8](https://github.com/julienandreu/zod-codegen/commit/04d6db8))
|
|
24
|
+
- docs: update Node.js version requirement in CONTRIBUTING.md ([16cdc1e](https://github.com/julienandreu/zod-codegen/commit/16cdc1e))
|
|
25
|
+
- docs: update PR description with latest test count ([f2ef4a8](https://github.com/julienandreu/zod-codegen/commit/f2ef4a8))
|
|
26
|
+
- test: add comprehensive test suite to increase confidence ([48a7e2d](https://github.com/julienandreu/zod-codegen/commit/48a7e2d))
|
|
27
|
+
- test: add tests for buildBasicTypeFromSchema edge cases ([c567c51](https://github.com/julienandreu/zod-codegen/commit/c567c51))
|
|
28
|
+
- test: improve code coverage for code-generator.service.ts ([edbc92a](https://github.com/julienandreu/zod-codegen/commit/edbc92a))
|
|
29
|
+
- refactor: completely remove policy system ([7c6439a](https://github.com/julienandreu/zod-codegen/commit/7c6439a))
|
|
30
|
+
- refactor: implement quality improvements ([a5b1b14](https://github.com/julienandreu/zod-codegen/commit/a5b1b14))
|
|
31
|
+
- refactor: remove policies export, keep zod-codegen as dev-dependency ([4eae513](https://github.com/julienandreu/zod-codegen/commit/4eae513))
|
|
32
|
+
- refactor: remove unused code and improve Reporter ([51ae06a](https://github.com/julienandreu/zod-codegen/commit/51ae06a))
|
|
33
|
+
- feat: add response handling policies system ([ad98229](https://github.com/julienandreu/zod-codegen/commit/ad98229))
|
|
34
|
+
|
|
1
35
|
## <small>1.5.1 (2026-01-05)</small>
|
|
2
36
|
|
|
3
37
|
- Merge pull request #52 from julienandreu/dependabot/npm_and_yarn/production-dependencies-3204786a64 ([c2fa562](https://github.com/julienandreu/zod-codegen/commit/c2fa562)), closes [#52](https://github.com/julienandreu/zod-codegen/issues/52)
|
package/CONTRIBUTING.md
CHANGED
package/EXAMPLES.md
CHANGED
|
@@ -13,7 +13,7 @@ All examples below demonstrate how to extend the generated client class to add t
|
|
|
13
13
|
### Example: Adding Bearer Token Authentication
|
|
14
14
|
|
|
15
15
|
```typescript
|
|
16
|
-
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type
|
|
16
|
+
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type';
|
|
17
17
|
|
|
18
18
|
class AuthenticatedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
19
19
|
private accessToken: string | null = null;
|
|
@@ -69,7 +69,7 @@ void main();
|
|
|
69
69
|
### Example: Session Management with Token Refresh
|
|
70
70
|
|
|
71
71
|
```typescript
|
|
72
|
-
import {SwaggerPetstoreOpenAPI30} from './generated/type
|
|
72
|
+
import {SwaggerPetstoreOpenAPI30} from './generated/type';
|
|
73
73
|
|
|
74
74
|
class SessionManagedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
75
75
|
private accessToken: string | null = null;
|
|
@@ -132,7 +132,7 @@ class SessionManagedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
132
132
|
If you need to pass custom headers for specific requests, you can extend the client and add helper methods:
|
|
133
133
|
|
|
134
134
|
```typescript
|
|
135
|
-
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type
|
|
135
|
+
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type';
|
|
136
136
|
|
|
137
137
|
class CustomHeadersPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
138
138
|
constructor(options: ClientOptions = {}) {
|
|
@@ -159,7 +159,7 @@ class CustomHeadersPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
159
159
|
### Example: API Key Authentication
|
|
160
160
|
|
|
161
161
|
```typescript
|
|
162
|
-
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type
|
|
162
|
+
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type';
|
|
163
163
|
|
|
164
164
|
class ApiKeyAuthenticatedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
165
165
|
constructor(options: ClientOptions & {apiKey: string}) {
|
|
@@ -185,7 +185,7 @@ class ApiKeyAuthenticatedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
|
185
185
|
### Example: Using AbortController for Request Cancellation
|
|
186
186
|
|
|
187
187
|
```typescript
|
|
188
|
-
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type
|
|
188
|
+
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type';
|
|
189
189
|
|
|
190
190
|
class CancellablePetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
191
191
|
constructor(options: ClientOptions = {}) {
|
|
@@ -220,7 +220,7 @@ client.cancelRequests();
|
|
|
220
220
|
### Example: Custom Credentials and CORS Mode
|
|
221
221
|
|
|
222
222
|
```typescript
|
|
223
|
-
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type
|
|
223
|
+
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type';
|
|
224
224
|
|
|
225
225
|
class CustomCorsPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
226
226
|
constructor(options: ClientOptions = {}) {
|
|
@@ -249,7 +249,7 @@ grep
|
|
|
249
249
|
Here's a comprehensive example showing how to combine CORS settings, custom User-Agent, and authentication:
|
|
250
250
|
|
|
251
251
|
```typescript
|
|
252
|
-
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type
|
|
252
|
+
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type';
|
|
253
253
|
|
|
254
254
|
class FullyConfiguredPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
255
255
|
private accessToken: string | null = null;
|
|
@@ -315,7 +315,7 @@ const pets = await client.findPetsByStatus('available');
|
|
|
315
315
|
You can also create different configurations for different environments:
|
|
316
316
|
|
|
317
317
|
```typescript
|
|
318
|
-
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type
|
|
318
|
+
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './generated/type';
|
|
319
319
|
|
|
320
320
|
interface ClientConfig {
|
|
321
321
|
userAgent?: string;
|
|
@@ -595,7 +595,7 @@ class RobustAPI extends YourAPI {
|
|
|
595
595
|
### Pattern 1: Simple Authentication
|
|
596
596
|
|
|
597
597
|
```typescript
|
|
598
|
-
import {YourAPI, ClientOptions} from './generated/type
|
|
598
|
+
import {YourAPI, ClientOptions} from './generated/type';
|
|
599
599
|
|
|
600
600
|
class SimpleAuthAPI extends YourAPI {
|
|
601
601
|
private token: string | null = null;
|
|
@@ -620,7 +620,7 @@ class SimpleAuthAPI extends YourAPI {
|
|
|
620
620
|
### Pattern 2: Multiple Headers
|
|
621
621
|
|
|
622
622
|
```typescript
|
|
623
|
-
import {YourAPI, ClientOptions} from './generated/type
|
|
623
|
+
import {YourAPI, ClientOptions} from './generated/type';
|
|
624
624
|
|
|
625
625
|
class MultiHeaderAPI extends YourAPI {
|
|
626
626
|
constructor(options: ClientOptions = {}) {
|
|
@@ -645,7 +645,7 @@ class MultiHeaderAPI extends YourAPI {
|
|
|
645
645
|
### Pattern 3: Conditional Options
|
|
646
646
|
|
|
647
647
|
```typescript
|
|
648
|
-
import {YourAPI, ClientOptions} from './generated/type
|
|
648
|
+
import {YourAPI, ClientOptions} from './generated/type';
|
|
649
649
|
|
|
650
650
|
class ConditionalAPI extends YourAPI {
|
|
651
651
|
constructor(options: ClientOptions = {}) {
|
|
@@ -702,7 +702,7 @@ return {
|
|
|
702
702
|
**Solution**: Store token as instance property:
|
|
703
703
|
|
|
704
704
|
```typescript
|
|
705
|
-
import {YourAPI, ClientOptions} from './generated/type
|
|
705
|
+
import {YourAPI, ClientOptions} from './generated/type';
|
|
706
706
|
|
|
707
707
|
class MyAPI extends YourAPI {
|
|
708
708
|
private token: string | null = null; // ✅ Instance property
|
|
@@ -728,3 +728,82 @@ protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>>
|
|
|
728
728
|
// ✅ Correct return type
|
|
729
729
|
}
|
|
730
730
|
```
|
|
731
|
+
|
|
732
|
+
## Response Handling
|
|
733
|
+
|
|
734
|
+
The generated client includes a protected `handleResponse()` method that you can override to implement custom response handling logic such as retries, logging, and error transformation.
|
|
735
|
+
|
|
736
|
+
### Understanding handleResponse
|
|
737
|
+
|
|
738
|
+
The `handleResponse()` method is called **before** error checking, allowing you to:
|
|
739
|
+
|
|
740
|
+
- Intercept responses and handle special cases
|
|
741
|
+
- Implement retry logic for specific status codes
|
|
742
|
+
- Log requests and responses
|
|
743
|
+
- Transform or modify responses
|
|
744
|
+
|
|
745
|
+
**Method Signature:**
|
|
746
|
+
|
|
747
|
+
```typescript
|
|
748
|
+
protected async handleResponse<T>(
|
|
749
|
+
response: Response,
|
|
750
|
+
method: string,
|
|
751
|
+
path: string,
|
|
752
|
+
options: {
|
|
753
|
+
params?: Record<string, string | number | boolean>;
|
|
754
|
+
data?: unknown;
|
|
755
|
+
contentType?: string;
|
|
756
|
+
headers?: Record<string, string>;
|
|
757
|
+
},
|
|
758
|
+
): Promise<Response>
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
### Example: Basic Retry Handler
|
|
762
|
+
|
|
763
|
+
```typescript
|
|
764
|
+
import {SwaggerPetstoreOpenAPI30} from './generated/type';
|
|
765
|
+
|
|
766
|
+
class PetstoreClientWithRetry extends SwaggerPetstoreOpenAPI30 {
|
|
767
|
+
private retrying = false;
|
|
768
|
+
|
|
769
|
+
protected async handleResponse<T>(
|
|
770
|
+
response: Response,
|
|
771
|
+
method: string,
|
|
772
|
+
path: string,
|
|
773
|
+
options: {...},
|
|
774
|
+
): Promise<Response> {
|
|
775
|
+
// Prevent infinite loops
|
|
776
|
+
if (this.retrying) {
|
|
777
|
+
return response;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// Handle 429 Too Many Requests
|
|
781
|
+
if (response.status === 429) {
|
|
782
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
783
|
+
const delay = retryAfter ? parseInt(retryAfter, 10) * 1000 : 1000;
|
|
784
|
+
|
|
785
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
786
|
+
await new Promise((resolve) => setTimeout(resolve, delay * (attempt + 1)));
|
|
787
|
+
|
|
788
|
+
this.retrying = true;
|
|
789
|
+
try {
|
|
790
|
+
const retryResponse = await this.retryRequest(method, path, options);
|
|
791
|
+
if (retryResponse.ok) {
|
|
792
|
+
return retryResponse;
|
|
793
|
+
}
|
|
794
|
+
} finally {
|
|
795
|
+
this.retrying = false;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
return response;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
private async retryRequest(...): Promise<Response> {
|
|
804
|
+
// Reconstruct and make the request
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
See [examples/petstore/retry-handler-usage.ts](./examples/petstore/retry-handler-usage.ts) for a complete example.
|
package/README.md
CHANGED
|
@@ -20,6 +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
24
|
- **🌐 Server Configuration**: Full support for OpenAPI server variables and templating (e.g., `{environment}.example.com`)
|
|
24
25
|
- **⚙️ Flexible Client Options**: Options-based constructor supporting server selection, variable overrides, and custom base URLs
|
|
25
26
|
|
|
@@ -145,7 +146,9 @@ The generator creates a single TypeScript file (`type.ts`) containing:
|
|
|
145
146
|
- **API Client Class**: A type-safe client class with methods for each endpoint operation
|
|
146
147
|
- **Server Configuration**: `serverConfigurations` array and `defaultBaseUrl` constant extracted from OpenAPI servers
|
|
147
148
|
- **Client Options Type**: `ClientOptions` type for flexible server selection and variable overrides
|
|
148
|
-
- **Protected Extension
|
|
149
|
+
- **Protected Extension Points**:
|
|
150
|
+
- `getBaseRequestOptions()` method for customizing request options
|
|
151
|
+
- `handleResponse()` method for response handling (retries, circuit breakers, etc.)
|
|
149
152
|
|
|
150
153
|
### Generated Client Structure
|
|
151
154
|
|
|
@@ -164,6 +167,9 @@ export class YourAPI {
|
|
|
164
167
|
// Protected method - override to customize request options
|
|
165
168
|
protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>>;
|
|
166
169
|
|
|
170
|
+
// Protected method - override to handle responses (retries, circuit breakers, etc.)
|
|
171
|
+
protected async handleResponse<T>(response: Response, method: string, path: string, options: {...}): Promise<Response>;
|
|
172
|
+
|
|
167
173
|
// Private method - handles all HTTP requests
|
|
168
174
|
async #makeRequest<T>(method: string, path: string, options: {...}): Promise<T>;
|
|
169
175
|
|
|
@@ -289,7 +295,7 @@ export class UserAPI {
|
|
|
289
295
|
**Usage:**
|
|
290
296
|
|
|
291
297
|
```typescript
|
|
292
|
-
import {UserAPI, User} from './generated/type
|
|
298
|
+
import {UserAPI, User} from './generated/type';
|
|
293
299
|
|
|
294
300
|
// Use default server from OpenAPI spec
|
|
295
301
|
const client = new UserAPI({});
|
|
@@ -310,7 +316,7 @@ The generated client includes a protected `getBaseRequestOptions()` method that
|
|
|
310
316
|
#### Basic Authentication Example
|
|
311
317
|
|
|
312
318
|
```typescript
|
|
313
|
-
import {UserAPI, ClientOptions} from './generated/type
|
|
319
|
+
import {UserAPI, ClientOptions} from './generated/type';
|
|
314
320
|
|
|
315
321
|
class AuthenticatedUserAPI extends UserAPI {
|
|
316
322
|
private accessToken: string | null = null;
|
|
@@ -344,7 +350,7 @@ const user = await client.getUserById(123); // Includes Authorization header
|
|
|
344
350
|
#### Complete Configuration Example
|
|
345
351
|
|
|
346
352
|
```typescript
|
|
347
|
-
import {UserAPI, ClientOptions} from './generated/type
|
|
353
|
+
import {UserAPI, ClientOptions} from './generated/type';
|
|
348
354
|
|
|
349
355
|
class FullyConfiguredAPI extends UserAPI {
|
|
350
356
|
private accessToken: string | null = null;
|
|
@@ -403,6 +409,7 @@ See [EXAMPLES.md](EXAMPLES.md) for comprehensive examples including:
|
|
|
403
409
|
- CORS configuration
|
|
404
410
|
- Request cancellation with AbortController
|
|
405
411
|
- Environment-specific configurations
|
|
412
|
+
- Response handling with custom retry logic and error handling
|
|
406
413
|
|
|
407
414
|
## 📖 Examples
|
|
408
415
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"add-js-extensions.d.ts","sourceRoot":"","sources":["../../scripts/add-js-extensions.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { extname, join } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Recursively finds all .js files in a directory
|
|
5
|
+
*/
|
|
6
|
+
function findJsFiles(dir) {
|
|
7
|
+
const files = [];
|
|
8
|
+
const entries = readdirSync(dir);
|
|
9
|
+
for (const entry of entries) {
|
|
10
|
+
const fullPath = join(dir, entry);
|
|
11
|
+
const stat = statSync(fullPath);
|
|
12
|
+
if (stat.isDirectory()) {
|
|
13
|
+
files.push(...findJsFiles(fullPath));
|
|
14
|
+
}
|
|
15
|
+
else if (extname(entry) === '.js') {
|
|
16
|
+
files.push(fullPath);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return files;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Adds .js extensions to relative imports in a JavaScript file
|
|
23
|
+
*/
|
|
24
|
+
function addJsExtensions(content) {
|
|
25
|
+
// Match relative imports: './something' or '../something' but not './something.js' or node: imports
|
|
26
|
+
// Handles both single and double quotes
|
|
27
|
+
// Also handles type imports: import type { ... } from './something'
|
|
28
|
+
const importRegex = /from\s+(['"])(\.\.?\/[^'"]+?)(['"])/g;
|
|
29
|
+
return content.replace(importRegex, (match, quote, importPath, endQuote) => {
|
|
30
|
+
// Skip if already has an extension
|
|
31
|
+
if (importPath.endsWith('.js') || importPath.endsWith('.json')) {
|
|
32
|
+
return match;
|
|
33
|
+
}
|
|
34
|
+
// Add .js extension
|
|
35
|
+
return `from ${quote}${importPath}.js${endQuote}`;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Main function to process all JavaScript files in dist/src
|
|
40
|
+
*/
|
|
41
|
+
function main() {
|
|
42
|
+
const distDir = join(process.cwd(), 'dist', 'src');
|
|
43
|
+
try {
|
|
44
|
+
const jsFiles = findJsFiles(distDir);
|
|
45
|
+
if (jsFiles.length === 0) {
|
|
46
|
+
console.warn('No .js files found in dist/src');
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
let processedCount = 0;
|
|
50
|
+
for (const filePath of jsFiles) {
|
|
51
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
52
|
+
const updatedContent = addJsExtensions(content);
|
|
53
|
+
if (content !== updatedContent) {
|
|
54
|
+
writeFileSync(filePath, updatedContent, 'utf-8');
|
|
55
|
+
processedCount++;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
console.log(`✅ Added .js extensions to ${String(processedCount)} file(s)`);
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error('❌ Error processing files:', error);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
main();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
interface PackageJson {
|
|
2
|
+
name: string;
|
|
3
|
+
version: string;
|
|
4
|
+
description: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Type guard for the package.json object
|
|
8
|
+
*
|
|
9
|
+
* @param input Unknown input
|
|
10
|
+
* @returns true if the input is an event object
|
|
11
|
+
*/
|
|
12
|
+
export declare function isPackageJson(input: unknown): input is PackageJson;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=update-manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-manifest.d.ts","sourceRoot":"","sources":["../../scripts/update-manifest.ts"],"names":[],"mappings":"AAKA,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAclE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
/**
|
|
6
|
+
* Type guard for the package.json object
|
|
7
|
+
*
|
|
8
|
+
* @param input Unknown input
|
|
9
|
+
* @returns true if the input is an event object
|
|
10
|
+
*/
|
|
11
|
+
export function isPackageJson(input) {
|
|
12
|
+
const event = z
|
|
13
|
+
.object({
|
|
14
|
+
name: z.string(),
|
|
15
|
+
version: z.string(),
|
|
16
|
+
description: z.string(),
|
|
17
|
+
})
|
|
18
|
+
.strict()
|
|
19
|
+
.catchall(z.any())
|
|
20
|
+
.required();
|
|
21
|
+
const { success } = event.safeParse(input);
|
|
22
|
+
return success;
|
|
23
|
+
}
|
|
24
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
const sourcePath = resolve(__dirname, '..', 'package.json');
|
|
26
|
+
const data = JSON.parse(readFileSync(sourcePath, 'utf8'));
|
|
27
|
+
if (!isPackageJson(data)) {
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
const { name, version, description } = data;
|
|
31
|
+
const targetPath = resolve(__dirname, '..', 'src', 'assets', 'manifest.json');
|
|
32
|
+
writeFileSync(targetPath, JSON.stringify({ name, version, description }, null, 2));
|
|
33
|
+
process.exit(0);
|
package/dist/src/cli.js
CHANGED
|
@@ -37,12 +37,12 @@ if (!packageJsonPath) {
|
|
|
37
37
|
}
|
|
38
38
|
const packageData = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
39
39
|
const { name, description, version } = packageData;
|
|
40
|
-
const reporter = new Reporter(process.stdout);
|
|
40
|
+
const reporter = new Reporter(process.stdout, process.stderr);
|
|
41
41
|
const startTime = process.hrtime.bigint();
|
|
42
42
|
debug(`${name}:${String(process.pid)}`);
|
|
43
43
|
loudRejection();
|
|
44
|
-
handleSignals(process, startTime);
|
|
45
|
-
handleErrors(process, startTime);
|
|
44
|
+
handleSignals(process, startTime, reporter);
|
|
45
|
+
handleErrors(process, startTime, reporter);
|
|
46
46
|
const argv = yargs(hideBin(process.argv))
|
|
47
47
|
.scriptName(name)
|
|
48
48
|
.usage(`${description}\n\nUsage: $0 [options]`)
|
package/dist/src/generator.d.ts
CHANGED
|
@@ -1,7 +1,34 @@
|
|
|
1
|
-
import type { Reporter } from './utils/reporter
|
|
2
|
-
import type { GeneratorOptions } from './types/generator-options
|
|
3
|
-
export type { GeneratorOptions } from './types/generator-options
|
|
4
|
-
export type { NamingConvention, OperationDetails, OperationNameTransformer } from './utils/naming-convention
|
|
1
|
+
import type { Reporter } from './utils/reporter';
|
|
2
|
+
import type { GeneratorOptions } from './types/generator-options';
|
|
3
|
+
export type { GeneratorOptions } from './types/generator-options';
|
|
4
|
+
export type { NamingConvention, OperationDetails, OperationNameTransformer } from './utils/naming-convention';
|
|
5
|
+
/**
|
|
6
|
+
* Main generator class for creating TypeScript code from OpenAPI specifications.
|
|
7
|
+
*
|
|
8
|
+
* This class orchestrates the code generation process:
|
|
9
|
+
* 1. Reads the OpenAPI specification file (local or remote)
|
|
10
|
+
* 2. Parses and validates the specification
|
|
11
|
+
* 3. Generates TypeScript code with Zod schemas and type-safe API client
|
|
12
|
+
* 4. Writes the generated code to the output directory
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import {Generator} from 'zod-codegen';
|
|
17
|
+
* import {Reporter} from './utils/reporter';
|
|
18
|
+
*
|
|
19
|
+
* const reporter = new Reporter(process.stdout, process.stderr);
|
|
20
|
+
* const generator = new Generator(
|
|
21
|
+
* 'my-app',
|
|
22
|
+
* '1.0.0',
|
|
23
|
+
* reporter,
|
|
24
|
+
* './openapi.yaml',
|
|
25
|
+
* './generated',
|
|
26
|
+
* {namingConvention: 'camelCase'}
|
|
27
|
+
* );
|
|
28
|
+
*
|
|
29
|
+
* const exitCode = await generator.run();
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
5
32
|
export declare class Generator {
|
|
6
33
|
private readonly _name;
|
|
7
34
|
private readonly _version;
|
|
@@ -13,7 +40,22 @@ export declare class Generator {
|
|
|
13
40
|
private readonly codeGenerator;
|
|
14
41
|
private readonly fileWriter;
|
|
15
42
|
private readonly outputPath;
|
|
43
|
+
/**
|
|
44
|
+
* Creates a new Generator instance.
|
|
45
|
+
*
|
|
46
|
+
* @param _name - The name of the application/library (used in generated file headers)
|
|
47
|
+
* @param _version - The version of the application/library (used in generated file headers)
|
|
48
|
+
* @param reporter - Reporter instance for logging messages and errors
|
|
49
|
+
* @param inputPath - Path or URL to the OpenAPI specification file
|
|
50
|
+
* @param _outputDir - Directory where generated files will be written
|
|
51
|
+
* @param options - Optional configuration for code generation
|
|
52
|
+
*/
|
|
16
53
|
constructor(_name: string, _version: string, reporter: Reporter, inputPath: string, _outputDir: string, options?: GeneratorOptions);
|
|
54
|
+
/**
|
|
55
|
+
* Executes the code generation process.
|
|
56
|
+
*
|
|
57
|
+
* @returns Promise that resolves to an exit code (0 for success, 1 for failure)
|
|
58
|
+
*/
|
|
17
59
|
run(): Promise<number>;
|
|
18
60
|
private readFile;
|
|
19
61
|
private parseFile;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,
|
|
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"}
|
package/dist/src/generator.js
CHANGED
|
@@ -1,6 +1,33 @@
|
|
|
1
1
|
import { OpenApiFileParserService, SyncFileReaderService } from './services/file-reader.service.js';
|
|
2
2
|
import { TypeScriptCodeGeneratorService } from './services/code-generator.service.js';
|
|
3
3
|
import { SyncFileWriterService } from './services/file-writer.service.js';
|
|
4
|
+
/**
|
|
5
|
+
* Main generator class for creating TypeScript code from OpenAPI specifications.
|
|
6
|
+
*
|
|
7
|
+
* This class orchestrates the code generation process:
|
|
8
|
+
* 1. Reads the OpenAPI specification file (local or remote)
|
|
9
|
+
* 2. Parses and validates the specification
|
|
10
|
+
* 3. Generates TypeScript code with Zod schemas and type-safe API client
|
|
11
|
+
* 4. Writes the generated code to the output directory
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import {Generator} from 'zod-codegen';
|
|
16
|
+
* import {Reporter} from './utils/reporter.js';
|
|
17
|
+
*
|
|
18
|
+
* const reporter = new Reporter(process.stdout, process.stderr);
|
|
19
|
+
* const generator = new Generator(
|
|
20
|
+
* 'my-app',
|
|
21
|
+
* '1.0.0',
|
|
22
|
+
* reporter,
|
|
23
|
+
* './openapi.yaml',
|
|
24
|
+
* './generated',
|
|
25
|
+
* {namingConvention: 'camelCase'}
|
|
26
|
+
* );
|
|
27
|
+
*
|
|
28
|
+
* const exitCode = await generator.run();
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
4
31
|
export class Generator {
|
|
5
32
|
_name;
|
|
6
33
|
_version;
|
|
@@ -12,6 +39,16 @@ export class Generator {
|
|
|
12
39
|
codeGenerator;
|
|
13
40
|
fileWriter;
|
|
14
41
|
outputPath;
|
|
42
|
+
/**
|
|
43
|
+
* Creates a new Generator instance.
|
|
44
|
+
*
|
|
45
|
+
* @param _name - The name of the application/library (used in generated file headers)
|
|
46
|
+
* @param _version - The version of the application/library (used in generated file headers)
|
|
47
|
+
* @param reporter - Reporter instance for logging messages and errors
|
|
48
|
+
* @param inputPath - Path or URL to the OpenAPI specification file
|
|
49
|
+
* @param _outputDir - Directory where generated files will be written
|
|
50
|
+
* @param options - Optional configuration for code generation
|
|
51
|
+
*/
|
|
15
52
|
constructor(_name, _version, reporter, inputPath, _outputDir, options = {}) {
|
|
16
53
|
this._name = _name;
|
|
17
54
|
this._version = _version;
|
|
@@ -22,6 +59,11 @@ export class Generator {
|
|
|
22
59
|
this.outputPath = this.fileWriter.resolveOutputPath(this._outputDir);
|
|
23
60
|
this.codeGenerator = new TypeScriptCodeGeneratorService(options);
|
|
24
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Executes the code generation process.
|
|
64
|
+
*
|
|
65
|
+
* @returns Promise that resolves to an exit code (0 for success, 1 for failure)
|
|
66
|
+
*/
|
|
25
67
|
async run() {
|
|
26
68
|
try {
|
|
27
69
|
const rawSource = await this.readFile();
|
|
@@ -38,7 +80,7 @@ export class Generator {
|
|
|
38
80
|
else {
|
|
39
81
|
this.reporter.error('❌ An unknown error occurred');
|
|
40
82
|
}
|
|
41
|
-
return
|
|
83
|
+
return 1;
|
|
42
84
|
}
|
|
43
85
|
}
|
|
44
86
|
async readFile() {
|
|
@@ -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,
|
|
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"}
|