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,8 +1,8 @@
1
- import {describe, expect, it, vi, beforeEach} from 'vitest';
2
- import {SyncFileReaderService, OpenApiFileParserService} from '../../src/services/file-reader.service';
3
- import {join} from 'node:path';
4
- import {fileURLToPath} from 'node:url';
5
- import {dirname} from 'node:path';
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { OpenApiFileParserService, SyncFileReaderService } from '../../src/services/file-reader.service';
3
+ import { join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { dirname } from 'node:path';
6
6
 
7
7
  const __dirname = dirname(fileURLToPath(import.meta.url));
8
8
 
@@ -36,7 +36,7 @@ describe('SyncFileReaderService', () => {
36
36
  const originalFetch = global.fetch;
37
37
  global.fetch = vi.fn().mockResolvedValue({
38
38
  ok: true,
39
- text: async () => '{"openapi": "3.0.0", "info": {"title": "Test", "version": "1.0.0"}, "paths": {}}',
39
+ text: async () => '{"openapi": "3.0.0", "info": {"title": "Test", "version": "1.0.0"}, "paths": {}}'
40
40
  } as Response);
41
41
 
42
42
  const content = await reader.readFile('https://example.com/openapi.json');
@@ -50,7 +50,7 @@ describe('SyncFileReaderService', () => {
50
50
  const mockFetch = vi.fn().mockResolvedValue({
51
51
  ok: false,
52
52
  status: 404,
53
- statusText: 'Not Found',
53
+ statusText: 'Not Found'
54
54
  } as Response);
55
55
  global.fetch = mockFetch;
56
56
 
@@ -74,8 +74,8 @@ describe('OpenApiFileParserService', () => {
74
74
  it('should parse valid JSON OpenAPI spec', () => {
75
75
  const jsonSpec = JSON.stringify({
76
76
  openapi: '3.0.0',
77
- info: {title: 'Test API', version: '1.0.0'},
78
- paths: {},
77
+ info: { title: 'Test API', version: '1.0.0' },
78
+ paths: {}
79
79
  });
80
80
 
81
81
  const result = parser.parse(jsonSpec);
@@ -102,8 +102,8 @@ paths: {}
102
102
  it('should parse already parsed objects', () => {
103
103
  const spec = {
104
104
  openapi: '3.0.0',
105
- info: {title: 'Test API', version: '1.0.0'},
106
- paths: {},
105
+ info: { title: 'Test API', version: '1.0.0' },
106
+ paths: {}
107
107
  };
108
108
 
109
109
  const result = parser.parse(spec);
@@ -114,8 +114,8 @@ paths: {}
114
114
  it('should validate OpenAPI structure', () => {
115
115
  const invalidSpec = {
116
116
  openapi: '2.0.0', // Wrong version format
117
- info: {title: 'Test API', version: '1.0.0'},
118
- paths: {},
117
+ info: { title: 'Test API', version: '1.0.0' },
118
+ paths: {}
119
119
  };
120
120
 
121
121
  expect(() => parser.parse(invalidSpec)).toThrow();
@@ -125,7 +125,7 @@ paths: {}
125
125
  const invalidSpec = {
126
126
  openapi: '3.0.0',
127
127
  // Missing info
128
- paths: {},
128
+ paths: {}
129
129
  };
130
130
 
131
131
  expect(() => parser.parse(invalidSpec)).toThrow();
@@ -1,10 +1,9 @@
1
- import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest';
2
- import {Generator} from '../../src/generator';
3
- import {Reporter} from '../../src/utils/reporter';
4
- import {readFileSync, existsSync, mkdirSync, rmSync} from 'node:fs';
5
- import {join} from 'node:path';
6
- import {fileURLToPath} from 'node:url';
7
- import {dirname} from 'node:path';
1
+ import { existsSync, mkdirSync, readFileSync, rmSync } 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
+ import { Reporter } from '../../src/utils/reporter';
8
7
 
9
8
  const __dirname = dirname(fileURLToPath(import.meta.url));
10
9
  const testOutputDir = join(__dirname, '../../test-output');
@@ -16,21 +15,22 @@ describe('Generator', () => {
16
15
  beforeEach(() => {
17
16
  // Clean up test output directory
18
17
  if (existsSync(testOutputDir)) {
19
- rmSync(testOutputDir, {recursive: true, force: true});
18
+ rmSync(testOutputDir, { recursive: true, force: true });
20
19
  }
21
- mkdirSync(testOutputDir, {recursive: true});
20
+
21
+ mkdirSync(testOutputDir, { recursive: true });
22
22
 
23
23
  // Create a mock reporter
24
24
  mockReporter = {
25
25
  log: vi.fn(),
26
- error: vi.fn(),
26
+ error: vi.fn()
27
27
  } as unknown as Reporter;
28
28
  });
29
29
 
30
30
  afterEach(() => {
31
31
  // Clean up test output directory
32
32
  if (existsSync(testOutputDir)) {
33
- rmSync(testOutputDir, {recursive: true, force: true});
33
+ rmSync(testOutputDir, { recursive: true, force: true });
34
34
  }
35
35
  });
36
36
 
@@ -62,7 +62,7 @@ describe('Generator', () => {
62
62
  expect(mockReporter.error).not.toHaveBeenCalled();
63
63
 
64
64
  // Verify output file was created
65
- const outputFile = join(testOutputDir, 'type.ts');
65
+ const outputFile = join(testOutputDir, 'api.ts');
66
66
  expect(existsSync(outputFile)).toBe(true);
67
67
 
68
68
  // Verify file contains expected content
@@ -84,9 +84,8 @@ describe('Generator', () => {
84
84
  it('should handle invalid OpenAPI specifications', async () => {
85
85
  // Create a temporary invalid OpenAPI file
86
86
  const invalidFile = join(testOutputDir, 'invalid.yaml');
87
- mkdirSync(testOutputDir, {recursive: true});
88
- readFileSync; // Ensure we can write
89
- const {writeFileSync} = await import('node:fs');
87
+ mkdirSync(testOutputDir, { recursive: true });
88
+ const { writeFileSync } = await import('node:fs');
90
89
  writeFileSync(invalidFile, 'invalid: yaml: content: [unclosed');
91
90
 
92
91
  generator = new Generator('test-app', '1.0.0', mockReporter, invalidFile, testOutputDir);
@@ -102,7 +101,7 @@ describe('Generator', () => {
102
101
 
103
102
  await generator.run();
104
103
 
105
- const outputFile = join(testOutputDir, 'type.ts');
104
+ const outputFile = join(testOutputDir, 'api.ts');
106
105
  const content = readFileSync(outputFile, 'utf-8');
107
106
 
108
107
  // Check for key components
@@ -117,7 +116,7 @@ describe('Generator', () => {
117
116
 
118
117
  await generator.run();
119
118
 
120
- const outputFile = join(testOutputDir, 'type.ts');
119
+ const outputFile = join(testOutputDir, 'api.ts');
121
120
  const content = readFileSync(outputFile, 'utf-8');
122
121
 
123
122
  expect(content).toContain('AUTOGENERATED');
@@ -125,11 +124,24 @@ describe('Generator', () => {
125
124
  expect(content).toContain('eslint-disable');
126
125
  });
127
126
 
127
+ it('should write to custom file path when output is a .ts path', async () => {
128
+ const customPath = join(testOutputDir, 'custom.ts');
129
+ generator = new Generator('test-app', '1.0.0', mockReporter, './samples/swagger-petstore.yaml', customPath);
130
+
131
+ const exitCode = await generator.run();
132
+
133
+ expect(exitCode).toBe(0);
134
+ expect(existsSync(customPath)).toBe(true);
135
+ expect(existsSync(join(testOutputDir, 'api.ts'))).toBe(false);
136
+ const content = readFileSync(customPath, 'utf-8');
137
+ expect(content).toMatch(/import\s*{\s*z\s*}\s*from\s*['"]zod['"]/);
138
+ });
139
+
128
140
  it('should handle unknown error type (not Error instance)', async () => {
129
141
  generator = new Generator('test-app', '1.0.0', mockReporter, './samples/swagger-petstore.yaml', testOutputDir);
130
142
 
131
143
  // Mock the fileReader to throw a non-Error object
132
- const {SyncFileReaderService} = await import('../../src/services/file-reader.service');
144
+ const { SyncFileReaderService } = await import('../../src/services/file-reader.service');
133
145
  const originalReadFile = SyncFileReaderService.prototype.readFile;
134
146
  SyncFileReaderService.prototype.readFile = vi.fn().mockRejectedValue('string error');
135
147
 
@@ -1,38 +1,38 @@
1
- import {describe, expect, it} from 'vitest';
2
- import {transformNamingConvention, type NamingConvention} from '../../src/utils/naming-convention';
1
+ import { describe, expect, it } from 'vitest';
2
+ import { type NamingConvention, transformNamingConvention } from '../../src/utils/naming-convention';
3
3
 
4
4
  describe('transformNamingConvention', () => {
5
5
  describe('transform', () => {
6
- const testCases: Array<{
6
+ const testCases: {
7
7
  input: string;
8
8
  convention: NamingConvention;
9
9
  expected: string;
10
10
  description: string;
11
- }> = [
11
+ }[] = [
12
12
  // camelCase
13
13
  {
14
14
  input: 'get_user_by_id',
15
15
  convention: 'camelCase',
16
16
  expected: 'getUserById',
17
- description: 'should convert snake_case to camelCase',
17
+ description: 'should convert snake_case to camelCase'
18
18
  },
19
19
  {
20
20
  input: 'GetUserById',
21
21
  convention: 'camelCase',
22
22
  expected: 'getUserById',
23
- description: 'should convert PascalCase to camelCase',
23
+ description: 'should convert PascalCase to camelCase'
24
24
  },
25
25
  {
26
26
  input: 'get-user-by-id',
27
27
  convention: 'camelCase',
28
28
  expected: 'getUserById',
29
- description: 'should convert kebab-case to camelCase',
29
+ description: 'should convert kebab-case to camelCase'
30
30
  },
31
31
  {
32
32
  input: 'getUserById',
33
33
  convention: 'camelCase',
34
34
  expected: 'getUserById',
35
- description: 'should keep camelCase as camelCase',
35
+ description: 'should keep camelCase as camelCase'
36
36
  },
37
37
 
38
38
  // PascalCase
@@ -40,19 +40,19 @@ describe('transformNamingConvention', () => {
40
40
  input: 'get_user_by_id',
41
41
  convention: 'PascalCase',
42
42
  expected: 'GetUserById',
43
- description: 'should convert snake_case to PascalCase',
43
+ description: 'should convert snake_case to PascalCase'
44
44
  },
45
45
  {
46
46
  input: 'get-user-by-id',
47
47
  convention: 'PascalCase',
48
48
  expected: 'GetUserById',
49
- description: 'should convert kebab-case to PascalCase',
49
+ description: 'should convert kebab-case to PascalCase'
50
50
  },
51
51
  {
52
52
  input: 'getUserById',
53
53
  convention: 'PascalCase',
54
54
  expected: 'GetUserById',
55
- description: 'should convert camelCase to PascalCase',
55
+ description: 'should convert camelCase to PascalCase'
56
56
  },
57
57
 
58
58
  // snake_case
@@ -60,19 +60,19 @@ describe('transformNamingConvention', () => {
60
60
  input: 'getUserById',
61
61
  convention: 'snake_case',
62
62
  expected: 'get_user_by_id',
63
- description: 'should convert camelCase to snake_case',
63
+ description: 'should convert camelCase to snake_case'
64
64
  },
65
65
  {
66
66
  input: 'GetUserById',
67
67
  convention: 'snake_case',
68
68
  expected: 'get_user_by_id',
69
- description: 'should convert PascalCase to snake_case',
69
+ description: 'should convert PascalCase to snake_case'
70
70
  },
71
71
  {
72
72
  input: 'get-user-by-id',
73
73
  convention: 'snake_case',
74
74
  expected: 'get_user_by_id',
75
- description: 'should convert kebab-case to snake_case',
75
+ description: 'should convert kebab-case to snake_case'
76
76
  },
77
77
 
78
78
  // kebab-case
@@ -80,19 +80,19 @@ describe('transformNamingConvention', () => {
80
80
  input: 'getUserById',
81
81
  convention: 'kebab-case',
82
82
  expected: 'get-user-by-id',
83
- description: 'should convert camelCase to kebab-case',
83
+ description: 'should convert camelCase to kebab-case'
84
84
  },
85
85
  {
86
86
  input: 'GetUserById',
87
87
  convention: 'kebab-case',
88
88
  expected: 'get-user-by-id',
89
- description: 'should convert PascalCase to kebab-case',
89
+ description: 'should convert PascalCase to kebab-case'
90
90
  },
91
91
  {
92
92
  input: 'get_user_by_id',
93
93
  convention: 'kebab-case',
94
94
  expected: 'get-user-by-id',
95
- description: 'should convert snake_case to kebab-case',
95
+ description: 'should convert snake_case to kebab-case'
96
96
  },
97
97
 
98
98
  // SCREAMING_SNAKE_CASE
@@ -100,13 +100,13 @@ describe('transformNamingConvention', () => {
100
100
  input: 'getUserById',
101
101
  convention: 'SCREAMING_SNAKE_CASE',
102
102
  expected: 'GET_USER_BY_ID',
103
- description: 'should convert camelCase to SCREAMING_SNAKE_CASE',
103
+ description: 'should convert camelCase to SCREAMING_SNAKE_CASE'
104
104
  },
105
105
  {
106
106
  input: 'get-user-by-id',
107
107
  convention: 'SCREAMING_SNAKE_CASE',
108
108
  expected: 'GET_USER_BY_ID',
109
- description: 'should convert kebab-case to SCREAMING_SNAKE_CASE',
109
+ description: 'should convert kebab-case to SCREAMING_SNAKE_CASE'
110
110
  },
111
111
 
112
112
  // SCREAMING-KEBAB-CASE
@@ -114,13 +114,13 @@ describe('transformNamingConvention', () => {
114
114
  input: 'getUserById',
115
115
  convention: 'SCREAMING-KEBAB-CASE',
116
116
  expected: 'GET-USER-BY-ID',
117
- description: 'should convert camelCase to SCREAMING-KEBAB-CASE',
117
+ description: 'should convert camelCase to SCREAMING-KEBAB-CASE'
118
118
  },
119
119
  {
120
120
  input: 'get_user_by_id',
121
121
  convention: 'SCREAMING-KEBAB-CASE',
122
122
  expected: 'GET-USER-BY-ID',
123
- description: 'should convert snake_case to SCREAMING-KEBAB-CASE',
123
+ description: 'should convert snake_case to SCREAMING-KEBAB-CASE'
124
124
  },
125
125
 
126
126
  // Edge cases
@@ -128,29 +128,29 @@ describe('transformNamingConvention', () => {
128
128
  input: '',
129
129
  convention: 'camelCase',
130
130
  expected: '',
131
- description: 'should handle empty string',
131
+ description: 'should handle empty string'
132
132
  },
133
133
  {
134
134
  input: 'a',
135
135
  convention: 'camelCase',
136
136
  expected: 'a',
137
- description: 'should handle single character',
137
+ description: 'should handle single character'
138
138
  },
139
139
  {
140
140
  input: 'API',
141
141
  convention: 'camelCase',
142
142
  expected: 'aPI',
143
- description: 'should handle all uppercase (splits at uppercase boundaries)',
143
+ description: 'should handle all uppercase (splits at uppercase boundaries)'
144
144
  },
145
145
  {
146
146
  input: 'getUser123ById',
147
147
  convention: 'snake_case',
148
148
  expected: 'get_user_123_by_id',
149
- description: 'should handle numbers in identifiers (splits at digit boundaries)',
150
- },
149
+ description: 'should handle numbers in identifiers (splits at digit boundaries)'
150
+ }
151
151
  ];
152
152
 
153
- testCases.forEach(({input, convention, expected, description}) => {
153
+ testCases.forEach(({ input, convention, expected, description }) => {
154
154
  it(description, () => {
155
155
  const result = transformNamingConvention(input, convention);
156
156
  expect(result).toBe(expected);
@@ -1,5 +1,5 @@
1
- import {describe, expect, it, beforeEach} from 'vitest';
2
- import {TypeScriptTypeBuilderService} from '../../src/services/type-builder.service';
1
+ import { beforeEach, describe, expect, it } from 'vitest';
2
+ import { TypeScriptTypeBuilderService } from '../../src/services/type-builder.service';
3
3
  import * as ts from 'typescript';
4
4
 
5
5
  describe('TypeScriptTypeBuilderService', () => {
package/tsconfig.json CHANGED
@@ -4,17 +4,19 @@
4
4
  "alwaysStrict": true,
5
5
  "baseUrl": ".",
6
6
  "checkJs": false,
7
+ "declaration": true,
8
+ "declarationMap": true,
7
9
  "downlevelIteration": true,
10
+ "emitDecoratorMetadata": true,
8
11
  "esModuleInterop": true,
9
12
  "exactOptionalPropertyTypes": true,
13
+ "experimentalDecorators": true,
10
14
  "forceConsistentCasingInFileNames": true,
11
15
  "incremental": true,
12
16
  "isolatedModules": true,
13
17
  "lib": ["ES2022", "DOM"],
14
18
  "module": "ESNext",
15
19
  "moduleResolution": "bundler",
16
- "declaration": true,
17
- "declarationMap": true,
18
20
  "noEmit": false,
19
21
  "noFallthroughCasesInSwitch": true,
20
22
  "noImplicitAny": true,
@@ -41,6 +43,6 @@
41
43
  "useUnknownInCatchVariables": true,
42
44
  "verbatimModuleSyntax": false
43
45
  },
44
- "exclude": ["node_modules", "dist", "build", "examples/**/*.ts", "tests/**/*.ts"],
46
+ "exclude": ["node_modules", "dist", "build", "examples"],
45
47
  "include": ["src/**/*.ts", "scripts/**/*.ts", "tests/**/*.ts", "vitest.config.ts"]
46
48
  }
package/vitest.config.ts CHANGED
@@ -1,39 +1,29 @@
1
- import {defineConfig} from 'vitest/config';
2
- import {resolve} from 'node:path';
1
+ import { resolve } from 'node:path';
2
+ import { defineConfig } from 'vitest/config';
3
3
 
4
4
  export default defineConfig({
5
5
  test: {
6
6
  globals: true,
7
7
  environment: 'node',
8
- include: ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
8
+ include: ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts}'],
9
9
  exclude: ['node_modules', 'dist', 'coverage'],
10
10
  coverage: {
11
11
  provider: 'v8',
12
12
  reporter: ['text', 'json', 'html', 'lcov'],
13
- exclude: [
14
- 'coverage/**',
15
- 'dist/**',
16
- 'node_modules/**',
17
- '**/*.d.ts',
18
- '**/*.config.*',
19
- '**/*.test.*',
20
- '**/*.spec.*',
21
- 'scripts/**',
22
- 'samples/**',
23
- ],
13
+ exclude: ['coverage/**', 'dist/**', 'node_modules/**', '**/*.d.ts', '**/*.config.*', '**/*.test.*', '**/*.spec.*', 'scripts/**', 'samples/**'],
24
14
  thresholds: {
25
15
  global: {
26
16
  branches: 80,
27
17
  functions: 80,
28
18
  lines: 80,
29
- statements: 80,
30
- },
31
- },
32
- },
19
+ statements: 80
20
+ }
21
+ }
22
+ }
33
23
  },
34
24
  resolve: {
35
25
  alias: {
36
- '@': resolve(__dirname, 'src'),
37
- },
38
- },
26
+ '@': resolve(__dirname, 'src')
27
+ }
28
+ }
39
29
  });
@@ -1,14 +0,0 @@
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
@@ -1 +0,0 @@
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"}
@@ -1,33 +0,0 @@
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);
@@ -1,5 +0,0 @@
1
- {
2
- "name": "zod-codegen",
3
- "version": "1.5.0",
4
- "description": "A powerful TypeScript code generator that creates Zod schemas and type-safe clients from OpenAPI specifications"
5
- }
@@ -1,109 +0,0 @@
1
- // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2
- // Built with zod-codegen@1.0.1
3
- // Latest edit: Thu, 13 Nov 2025 13:32:35 GMT
4
- // Source file: ./samples/pokeapi-openapi.json
5
- /* eslint-disable */
6
- // @ts-nocheck
7
-
8
- // Imports
9
- import {z} from 'zod';
10
-
11
- // Components schemas
12
- export const NamedAPIResource = z.object({
13
- name: z.string(),
14
- url: z.string().url(),
15
- });
16
- export const PokemonType = z.object({
17
- slot: z.number().int().optional(),
18
- type: NamedAPIResource.optional(),
19
- });
20
- export const PokemonAbility = z.object({
21
- ability: NamedAPIResource.optional(),
22
- is_hidden: z.boolean().optional(),
23
- slot: z.number().int().optional(),
24
- });
25
- export const PokemonSprites = z.object({
26
- front_default: z.string().url().optional(),
27
- front_shiny: z.string().url().optional(),
28
- back_default: z.string().url().optional(),
29
- back_shiny: z.string().url().optional(),
30
- });
31
- export const Pokemon = z.object({
32
- id: z.number().int(),
33
- name: z.string(),
34
- height: z.number().int().optional(),
35
- weight: z.number().int().optional(),
36
- base_experience: z.number().int().optional(),
37
- types: z.array(PokemonType).optional(),
38
- abilities: z.array(PokemonAbility).optional(),
39
- sprites: PokemonSprites.optional(),
40
- });
41
- export const PokemonListResponse = z.object({
42
- count: z.number().int(),
43
- next: z.string().url().optional(),
44
- previous: z.string().url().optional(),
45
- results: z.array(NamedAPIResource),
46
- });
47
- export const defaultBaseUrl = 'https://pokeapi.co/api/v2';
48
-
49
- // Client class
50
- export class PokAPI {
51
- readonly #baseUrl: string;
52
- constructor(baseUrl: string = defaultBaseUrl, _?: unknown) {
53
- this.#baseUrl = baseUrl;
54
- }
55
- protected getBaseRequestOptions(): Partial<Omit<RequestInit, 'method' | 'body'>> {
56
- return {};
57
- }
58
- async #makeRequest<T>(
59
- method: string,
60
- path: string,
61
- options: _params___Record_string__string___number___boolean___data___unknown__contentType___string__headers___Record_string__string__ = {},
62
- ): Promise<T> {
63
- const baseUrl = `${this.#baseUrl}${path}`;
64
- const url =
65
- options.params && Object.keys(options.params).length > 0
66
- ? (() => {
67
- Object.entries(options.params).forEach(([key, value]) => {
68
- new URL(baseUrl).searchParams.set(key, String(value));
69
- });
70
- return new URL(baseUrl).toString();
71
- })()
72
- : new URL(baseUrl).toString();
73
- const baseOptions = this.getBaseRequestOptions();
74
- const contentType =
75
- options.contentType === 'application/x-www-form-urlencoded'
76
- ? 'application/x-www-form-urlencoded'
77
- : 'application/json';
78
- const baseHeaders = baseOptions.headers !== undefined ? baseOptions.headers : {};
79
- const headers = Object.assign(
80
- {},
81
- baseHeaders,
82
- {'Content-Type': contentType},
83
- options.headers !== undefined ? options.headers : {},
84
- );
85
- const body =
86
- options.data !== undefined
87
- ? options.contentType === 'application/x-www-form-urlencoded'
88
- ? (() => {
89
- const params = new URLSearchParams();
90
- Object.entries(options.data).forEach(([key, value]) => {
91
- params.set(key, String(value));
92
- });
93
- return params.toString();
94
- })()
95
- : JSON.stringify(options.data)
96
- : null;
97
- const response = await fetch(url, Object.assign({}, baseOptions, {method, headers: headers, body: body}));
98
- if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
99
- return await response.json();
100
- }
101
- async getPokemonById(id: string): Promise<Pokemon> {
102
- return Pokemon.parse(await this.#makeRequest('GET', `/pokemon/${id}`, {}));
103
- }
104
- async getPokemonList(limit?: number, offset?: number): Promise<PokemonListResponse> {
105
- return PokemonListResponse.parse(
106
- await this.#makeRequest('GET', '/pokemon', {params: {limit: limit, offset: offset}}),
107
- );
108
- }
109
- }