zod-codegen 1.6.1 → 1.6.3

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.
@@ -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
package/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## <small>1.6.3 (2026-01-22)</small>
2
+
3
+ - fix: do not depend on inference anymore and build strict types all ar… (#60) ([b0b663e](https://github.com/julienandreu/zod-codegen/commit/b0b663e)), closes [#60](https://github.com/julienandreu/zod-codegen/issues/60)
4
+
5
+ ## <small>1.6.2 (2026-01-19)</small>
6
+
7
+ - 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)
8
+ - 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)
9
+
1
10
  ## <small>1.6.1 (2026-01-09)</small>
2
11
 
3
12
  - 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)
@@ -17,6 +17,27 @@ export declare class TypeScriptCodeGeneratorService implements CodeGenerator, Sc
17
17
  private buildAST;
18
18
  private buildSchemas;
19
19
  private buildSchemaTypeAliases;
20
+ /**
21
+ * Builds explicit TypeScript type declarations for all schemas.
22
+ * Returns interface declarations for object types and type aliases for other types.
23
+ */
24
+ private buildExplicitTypeDeclarations;
25
+ /**
26
+ * Converts an OpenAPI schema to a TypeScript type node.
27
+ */
28
+ private buildTypeNode;
29
+ /**
30
+ * Builds the base type node without nullable handling.
31
+ */
32
+ private buildBaseTypeNode;
33
+ /**
34
+ * Builds a TypeScript type literal for an object schema.
35
+ */
36
+ private buildObjectTypeLiteral;
37
+ /**
38
+ * Builds a TypeScript interface declaration for an object schema.
39
+ */
40
+ private buildInterfaceDeclaration;
20
41
  private buildClientClass;
21
42
  private buildConstructor;
22
43
  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;IAmBhB,OAAO,CAAC,YAAY;IA2CpB,OAAO,CAAC,sBAAsB;IAe9B,OAAO,CAAC,gBAAgB;IAuBxB,OAAO,CAAC,gBAAgB;IAmGxB,OAAO,CAAC,gCAAgC;IAwBxC,OAAO,CAAC,yBAAyB;IAuBjC,OAAO,CAAC,sBAAsB;IAkoB9B,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":"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"}
@@ -53,9 +53,12 @@ export class TypeScriptCodeGeneratorService {
53
53
  const schemaTypeAliases = this.buildSchemaTypeAliases(schemas);
54
54
  const serverConfig = this.buildServerConfiguration(openapi);
55
55
  const clientClass = this.buildClientClass(openapi, schemas);
56
+ const explicitTypeDeclarations = this.buildExplicitTypeDeclarations(openapi);
56
57
  return [
57
58
  this.createComment('Imports'),
58
59
  ...imports,
60
+ this.createComment('Explicit type declarations'),
61
+ ...explicitTypeDeclarations,
59
62
  this.createComment('Components schemas'),
60
63
  ...Object.values(schemas),
61
64
  ...schemaTypeAliases,
@@ -79,8 +82,11 @@ export class TypeScriptCodeGeneratorService {
79
82
  const schemaExpression = this.buildSchema(schema);
80
83
  // Clear context
81
84
  this.currentSchemaName = null;
85
+ const sanitizedName = this.typeBuilder.sanitizeIdentifier(name);
86
+ // Add type annotation: z.ZodType<Name>
87
+ const typeAnnotation = ts.factory.createTypeReferenceNode(ts.factory.createQualifiedName(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('ZodType')), [ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedName), undefined)]);
82
88
  const variableStatement = ts.factory.createVariableStatement([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createVariableDeclarationList([
83
- ts.factory.createVariableDeclaration(ts.factory.createIdentifier(this.typeBuilder.sanitizeIdentifier(name)), undefined, undefined, schemaExpression),
89
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier(sanitizedName), undefined, typeAnnotation, schemaExpression),
84
90
  ], ts.NodeFlags.Const));
85
91
  return {
86
92
  ...schemaRegistered,
@@ -88,11 +94,157 @@ export class TypeScriptCodeGeneratorService {
88
94
  };
89
95
  }, {});
90
96
  }
91
- buildSchemaTypeAliases(schemas) {
92
- return Object.keys(schemas).map((name) => {
97
+ buildSchemaTypeAliases(_schemas) {
98
+ // Explicit type declarations are used instead of z.infer type exports
99
+ return [];
100
+ }
101
+ /**
102
+ * Builds explicit TypeScript type declarations for all schemas.
103
+ * Returns interface declarations for object types and type aliases for other types.
104
+ */
105
+ buildExplicitTypeDeclarations(openapi) {
106
+ const schemasEntries = Object.entries(openapi.components?.schemas ?? {});
107
+ const schemasMap = Object.fromEntries(schemasEntries);
108
+ const sortedSchemaNames = this.topologicalSort(schemasMap);
109
+ const statements = [];
110
+ for (const name of sortedSchemaNames) {
111
+ const schema = openapi.components?.schemas?.[name];
112
+ if (!schema)
113
+ continue;
93
114
  const sanitizedName = this.typeBuilder.sanitizeIdentifier(name);
94
- return ts.factory.createTypeAliasDeclaration([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier(sanitizedName), undefined, ts.factory.createTypeReferenceNode(ts.factory.createQualifiedName(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('infer')), [ts.factory.createTypeQueryNode(ts.factory.createIdentifier(sanitizedName), undefined)]));
115
+ const safeSchema = SchemaProperties.safeParse(schema);
116
+ if (!safeSchema.success) {
117
+ // Unknown schema type, create a type alias to unknown
118
+ statements.push(ts.factory.createTypeAliasDeclaration([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier(sanitizedName), undefined, ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword)));
119
+ continue;
120
+ }
121
+ const schemaData = safeSchema.data;
122
+ const typeNode = this.buildTypeNode(schemaData);
123
+ // For object types with properties, create an interface
124
+ if (schemaData['type'] === 'object' && schemaData['properties']) {
125
+ statements.push(this.buildInterfaceDeclaration(sanitizedName, schemaData));
126
+ continue;
127
+ }
128
+ // For all other types (enums, arrays, unions, etc.), create a type alias
129
+ statements.push(ts.factory.createTypeAliasDeclaration([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier(sanitizedName), undefined, typeNode));
130
+ }
131
+ return statements;
132
+ }
133
+ /**
134
+ * Converts an OpenAPI schema to a TypeScript type node.
135
+ */
136
+ buildTypeNode(schema) {
137
+ const safeSchema = SchemaProperties.safeParse(schema);
138
+ if (!safeSchema.success) {
139
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
140
+ }
141
+ const prop = safeSchema.data;
142
+ // Handle $ref
143
+ if (this.isReference(prop)) {
144
+ const { $ref = '' } = Reference.parse(prop);
145
+ const refName = $ref.split('/').pop() ?? 'never';
146
+ const sanitizedRefName = this.typeBuilder.sanitizeIdentifier(refName);
147
+ return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(sanitizedRefName), undefined);
148
+ }
149
+ // Handle nullable
150
+ const isNullable = prop['nullable'] === true;
151
+ const baseTypeNode = this.buildBaseTypeNode(prop);
152
+ if (isNullable) {
153
+ return ts.factory.createUnionTypeNode([baseTypeNode, ts.factory.createLiteralTypeNode(ts.factory.createNull())]);
154
+ }
155
+ return baseTypeNode;
156
+ }
157
+ /**
158
+ * Builds the base type node without nullable handling.
159
+ */
160
+ buildBaseTypeNode(prop) {
161
+ // Handle anyOf/oneOf (union types)
162
+ if (prop['anyOf'] && Array.isArray(prop['anyOf']) && prop['anyOf'].length > 0) {
163
+ const types = prop['anyOf'].map((s) => this.buildTypeNode(s));
164
+ return ts.factory.createUnionTypeNode(types);
165
+ }
166
+ if (prop['oneOf'] && Array.isArray(prop['oneOf']) && prop['oneOf'].length > 0) {
167
+ const types = prop['oneOf'].map((s) => this.buildTypeNode(s));
168
+ return ts.factory.createUnionTypeNode(types);
169
+ }
170
+ // Handle allOf (intersection types)
171
+ if (prop['allOf'] && Array.isArray(prop['allOf']) && prop['allOf'].length > 0) {
172
+ const types = prop['allOf'].map((s) => this.buildTypeNode(s));
173
+ return ts.factory.createIntersectionTypeNode(types);
174
+ }
175
+ // Handle enum
176
+ if (prop['enum'] && Array.isArray(prop['enum']) && prop['enum'].length > 0) {
177
+ const literalTypes = prop['enum'].map((val) => {
178
+ if (typeof val === 'string') {
179
+ return ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(val, true));
180
+ }
181
+ else if (typeof val === 'number') {
182
+ if (val < 0) {
183
+ return ts.factory.createLiteralTypeNode(ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, ts.factory.createNumericLiteral(String(Math.abs(val)))));
184
+ }
185
+ return ts.factory.createLiteralTypeNode(ts.factory.createNumericLiteral(String(val)));
186
+ }
187
+ else if (typeof val === 'boolean') {
188
+ return ts.factory.createLiteralTypeNode(val ? ts.factory.createTrue() : ts.factory.createFalse());
189
+ }
190
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
191
+ });
192
+ return ts.factory.createUnionTypeNode(literalTypes);
193
+ }
194
+ // Handle type-specific schemas
195
+ switch (prop['type']) {
196
+ case 'string':
197
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword);
198
+ case 'number':
199
+ case 'integer':
200
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword);
201
+ case 'boolean':
202
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
203
+ case 'array': {
204
+ const itemsType = prop['items']
205
+ ? this.buildTypeNode(prop['items'])
206
+ : ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
207
+ return ts.factory.createArrayTypeNode(itemsType);
208
+ }
209
+ case 'object': {
210
+ const properties = (prop['properties'] ?? {});
211
+ const requiredProps = (prop['required'] ?? []);
212
+ if (Object.keys(properties).length > 0) {
213
+ return this.buildObjectTypeLiteral(properties, requiredProps);
214
+ }
215
+ // Empty object or additionalProperties - use Record<string, unknown>
216
+ return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Record'), [
217
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
218
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
219
+ ]);
220
+ }
221
+ default:
222
+ return ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword);
223
+ }
224
+ }
225
+ /**
226
+ * Builds a TypeScript type literal for an object schema.
227
+ */
228
+ buildObjectTypeLiteral(properties, requiredProps) {
229
+ const members = Object.entries(properties).map(([name, propSchema]) => {
230
+ const isRequired = requiredProps.includes(name);
231
+ const typeNode = this.buildTypeNode(propSchema);
232
+ return ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier(name), isRequired ? undefined : ts.factory.createToken(ts.SyntaxKind.QuestionToken), typeNode);
233
+ });
234
+ return ts.factory.createTypeLiteralNode(members);
235
+ }
236
+ /**
237
+ * Builds a TypeScript interface declaration for an object schema.
238
+ */
239
+ buildInterfaceDeclaration(name, schema) {
240
+ const properties = (schema['properties'] ?? {});
241
+ const requiredProps = (schema['required'] ?? []);
242
+ const members = Object.entries(properties).map(([propName, propSchema]) => {
243
+ const isRequired = requiredProps.includes(propName);
244
+ const typeNode = this.buildTypeNode(propSchema);
245
+ return ts.factory.createPropertySignature(undefined, ts.factory.createIdentifier(propName), isRequired ? undefined : ts.factory.createToken(ts.SyntaxKind.QuestionToken), typeNode);
95
246
  });
247
+ return ts.factory.createInterfaceDeclaration([ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier(name), undefined, undefined, members);
96
248
  }
97
249
  buildClientClass(openapi, schemas) {
98
250
  const clientName = this.generateClientName(openapi.info.title);
@@ -161,22 +313,26 @@ export class TypeScriptCodeGeneratorService {
161
313
  ], ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
162
314
  ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('T'), undefined),
163
315
  ]), ts.factory.createBlock([
164
- // Build URL with query parameters
316
+ // Create initial URL object that we will use to build the final URL
165
317
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
166
- ts.factory.createVariableDeclaration(ts.factory.createIdentifier('baseUrl'), undefined, undefined, ts.factory.createTemplateExpression(ts.factory.createTemplateHead('', ''), [
167
- ts.factory.createTemplateSpan(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#baseUrl')), ts.factory.createTemplateMiddle('', '')),
168
- ts.factory.createTemplateSpan(ts.factory.createIdentifier('path'), ts.factory.createTemplateTail('', '')),
318
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier('baseUrl'), undefined, undefined, ts.factory.createNewExpression(ts.factory.createIdentifier('URL'), undefined, [
319
+ ts.factory.createIdentifier('path'),
320
+ ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#baseUrl')),
169
321
  ])),
170
322
  ], ts.NodeFlags.Const)),
171
323
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
172
324
  ts.factory.createVariableDeclaration(ts.factory.createIdentifier('url'), undefined, undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params')), ts.factory.createToken(ts.SyntaxKind.AmpersandAmpersandToken), ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('keys')), undefined, [
173
325
  ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params')),
174
- ]), ts.factory.createIdentifier('length')), ts.factory.createToken(ts.SyntaxKind.GreaterThanToken), ts.factory.createNumericLiteral('0'))), undefined, (() => {
175
- const urlObj = ts.factory.createNewExpression(ts.factory.createIdentifier('URL'), undefined, [
176
- ts.factory.createIdentifier('baseUrl'),
177
- ]);
178
- const forEachCall = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('entries')), undefined, [
326
+ ]), ts.factory.createIdentifier('length')), ts.factory.createToken(ts.SyntaxKind.GreaterThanToken), ts.factory.createNumericLiteral('0'))), undefined, ts.factory.createCallExpression(ts.factory.createParenthesizedExpression(ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
327
+ ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('entries')), undefined, [
179
328
  ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params')),
329
+ ]), ts.factory.createIdentifier('filter')), undefined, [
330
+ ts.factory.createArrowFunction(undefined, undefined, [
331
+ ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createArrayBindingPattern([
332
+ ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier(''), undefined),
333
+ ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('value'), undefined),
334
+ ]), undefined, undefined),
335
+ ], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createIdentifier('value'), ts.SyntaxKind.ExclamationEqualsEqualsToken, ts.factory.createIdentifier('undefined'))),
180
336
  ]), ts.factory.createIdentifier('forEach')), undefined, [
181
337
  ts.factory.createArrowFunction(undefined, undefined, [
182
338
  ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createArrayBindingPattern([
@@ -184,20 +340,14 @@ export class TypeScriptCodeGeneratorService {
184
340
  ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('value'), undefined),
185
341
  ]), undefined, undefined),
186
342
  ], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
187
- ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(urlObj, ts.factory.createIdentifier('searchParams')), ts.factory.createIdentifier('set')), undefined, [
343
+ ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseUrl'), ts.factory.createIdentifier('searchParams')), ts.factory.createIdentifier('set')), undefined, [
188
344
  ts.factory.createIdentifier('key'),
189
345
  ts.factory.createCallExpression(ts.factory.createIdentifier('String'), undefined, [ts.factory.createIdentifier('value')]),
190
346
  ])),
191
347
  ], false)),
192
- ]);
193
- // Use IIFE to execute forEach and return URL string
194
- return ts.factory.createCallExpression(ts.factory.createParenthesizedExpression(ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
195
- ts.factory.createExpressionStatement(forEachCall),
196
- ts.factory.createReturnStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(urlObj, ts.factory.createIdentifier('toString')), undefined, [])),
197
- ], false))), undefined, []);
198
- })(), undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createNewExpression(ts.factory.createIdentifier('URL'), undefined, [
199
- ts.factory.createIdentifier('baseUrl'),
200
- ]), ts.factory.createIdentifier('toString')), undefined, []))),
348
+ ])),
349
+ ts.factory.createReturnStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseUrl'), ts.factory.createIdentifier('toString')), undefined, [])),
350
+ ], true))), undefined, []), undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('baseUrl'), ts.factory.createIdentifier('toString')), undefined, []))),
201
351
  ], ts.NodeFlags.Const)),
202
352
  // Get base request options (headers, signal, credentials, etc.)
203
353
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
@@ -1,6 +1,6 @@
1
1
  // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2
- // Built with zod-codegen@1.5.0
3
- // Latest edit: Thu, 08 Jan 2026 19:31:58 GMT
2
+ // Built with zod-codegen@1.6.1
3
+ // Latest edit: Mon, 19 Jan 2026 14:27:35 GMT
4
4
  // Source file: ./samples/swagger-petstore.yaml
5
5
  /* eslint-disable */
6
6
  // @ts-nocheck
@@ -126,16 +126,18 @@ export default class SwaggerPetstoreOpenAPI30 {
126
126
  path: string,
127
127
  options: _params___Record_string__string___number___boolean___data___unknown__contentType___string__headers___Record_string__string__ = {},
128
128
  ): Promise<T> {
129
- const baseUrl = `${this.#baseUrl}${path}`;
129
+ const baseUrl = new URL(path, this.#baseUrl);
130
130
  const url =
131
131
  options.params && Object.keys(options.params).length > 0
132
132
  ? (() => {
133
- Object.entries(options.params).forEach(([key, value]) => {
134
- new URL(baseUrl).searchParams.set(key, String(value));
135
- });
136
- return new URL(baseUrl).toString();
133
+ Object.entries(options.params)
134
+ .filter(([, value]) => value !== undefined)
135
+ .forEach(([key, value]) => {
136
+ baseUrl.searchParams.set(key, String(value));
137
+ });
138
+ return baseUrl.toString();
137
139
  })()
138
- : new URL(baseUrl).toString();
140
+ : baseUrl.toString();
139
141
  const baseOptions = this.getBaseRequestOptions();
140
142
  const contentType =
141
143
  options.contentType === 'application/x-www-form-urlencoded'
package/generated/type.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  // THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2
- // Built with zod-codegen@1.6.1
3
- // Latest edit: Fri, 09 Jan 2026 16:41:29 GMT
2
+ // Built with zod-codegen@1.6.3
3
+ // Latest edit: Thu, 22 Jan 2026 16:08:32 GMT
4
4
  // Source file: ./samples/swagger-petstore.yaml
5
5
  /* eslint-disable */
6
6
  // @ts-nocheck
@@ -10,9 +10,62 @@
10
10
  ;
11
11
  import { z } from 'zod';
12
12
 
13
+ // Explicit type declarations
14
+ ;
15
+ export interface Order {
16
+ id?: number;
17
+ petId?: number;
18
+ quantity?: number;
19
+ shipDate?: string;
20
+ status?: 'placed' | 'approved' | 'delivered';
21
+ complete?: boolean;
22
+ }
23
+ export interface Address {
24
+ street?: string;
25
+ city?: string;
26
+ state?: string;
27
+ zip?: string;
28
+ }
29
+ export interface Customer {
30
+ id?: number;
31
+ username?: string;
32
+ address?: Address[];
33
+ }
34
+ export interface Category {
35
+ id?: number;
36
+ name?: string;
37
+ }
38
+ export interface User {
39
+ id?: number;
40
+ username?: string;
41
+ firstName?: string;
42
+ lastName?: string;
43
+ email?: string;
44
+ password?: string;
45
+ phone?: string;
46
+ userStatus?: number;
47
+ }
48
+ export interface Tag {
49
+ id?: number;
50
+ name?: string;
51
+ }
52
+ export interface Pet {
53
+ id?: number;
54
+ name: string;
55
+ category?: Category;
56
+ photoUrls: string[];
57
+ tags?: Tag[];
58
+ status?: 'available' | 'pending' | 'sold';
59
+ }
60
+ export interface ApiResponse {
61
+ code?: number;
62
+ type?: string;
63
+ message?: string;
64
+ }
65
+
13
66
  // Components schemas
14
67
  ;
15
- export const Order = z.object({
68
+ export const Order: z.ZodType<Order> = z.object({
16
69
  id: z.number().int().optional(),
17
70
  petId: z.number().int().optional(),
18
71
  quantity: z.number().int().optional(),
@@ -22,22 +75,22 @@ export const Order = z.object({
22
75
  status: z.enum(['placed', 'approved', 'delivered']).optional(),
23
76
  complete: z.boolean().optional()
24
77
  });
25
- export const Address = z.object({
78
+ export const Address: z.ZodType<Address> = z.object({
26
79
  street: z.string().optional(),
27
80
  city: z.string().optional(),
28
81
  state: z.string().optional(),
29
82
  zip: z.string().optional()
30
83
  });
31
- export const Customer = z.object({
84
+ export const Customer: z.ZodType<Customer> = z.object({
32
85
  id: z.number().int().optional(),
33
86
  username: z.string().optional(),
34
87
  address: z.array(Address).optional()
35
88
  });
36
- export const Category = z.object({
89
+ export const Category: z.ZodType<Category> = z.object({
37
90
  id: z.number().int().optional(),
38
91
  name: z.string().optional()
39
92
  });
40
- export const User = z.object({
93
+ export const User: z.ZodType<User> = z.object({
41
94
  id: z.number().int().optional(),
42
95
  username: z.string().optional(),
43
96
  firstName: z.string().optional(),
@@ -47,11 +100,11 @@ export const User = z.object({
47
100
  phone: z.string().optional(),
48
101
  userStatus: z.number().int().optional()
49
102
  });
50
- export const Tag = z.object({
103
+ export const Tag: z.ZodType<Tag> = z.object({
51
104
  id: z.number().int().optional(),
52
105
  name: z.string().optional()
53
106
  });
54
- export const Pet = z.object({
107
+ export const Pet: z.ZodType<Pet> = z.object({
55
108
  id: z.number().int().optional(),
56
109
  name: z.string(),
57
110
  category: Category.optional(),
@@ -59,19 +112,11 @@ export const Pet = z.object({
59
112
  tags: z.array(Tag).optional(),
60
113
  status: z.enum(['available', 'pending', 'sold']).optional()
61
114
  });
62
- export const ApiResponse = z.object({
115
+ export const ApiResponse: z.ZodType<ApiResponse> = z.object({
63
116
  code: z.number().int().optional(),
64
117
  type: z.string().optional(),
65
118
  message: z.string().optional()
66
119
  });
67
- export type Order = z.infer<typeof Order>;
68
- export type Address = z.infer<typeof Address>;
69
- export type Customer = z.infer<typeof Customer>;
70
- export type Category = z.infer<typeof Category>;
71
- export type User = z.infer<typeof User>;
72
- export type Tag = z.infer<typeof Tag>;
73
- export type Pet = z.infer<typeof Pet>;
74
- export type ApiResponse = z.infer<typeof ApiResponse>;
75
120
  export const serverConfigurations = [{
76
121
  url: 'https://petstore3.swagger.io/api/v3'
77
122
  }];
@@ -114,8 +159,11 @@ export default class SwaggerPetstoreOpenAPI30 {
114
159
  return response;
115
160
  }
116
161
  protected async makeRequest<T>(method: string, path: string, options: _params___Record_string__string___number___boolean___data___unknown__contentType___string__headers___Record_string__string__ = {}): Promise<T> {
117
- const baseUrl = `${this.#baseUrl}${path}`;
118
- const url = options.params && Object.keys(options.params).length > 0 ? (() => { Object.entries(options.params).forEach(([key, value]) => { new URL(baseUrl).searchParams.set(key, String(value)); }); return new URL(baseUrl).toString(); })() : new URL(baseUrl).toString();
162
+ const baseUrl = new URL(path, this.#baseUrl);
163
+ const url = options.params && Object.keys(options.params).length > 0 ? (() => {
164
+ Object.entries(options.params).filter(([, value]) => value !== undefined).forEach(([key, value]) => { baseUrl.searchParams.set(key, String(value)); });
165
+ return baseUrl.toString();
166
+ })() : baseUrl.toString();
119
167
  const baseOptions = this.getBaseRequestOptions();
120
168
  const contentType = options.contentType === 'application/x-www-form-urlencoded' ? 'application/x-www-form-urlencoded' : 'application/json';
121
169
  const baseHeaders = baseOptions.headers !== undefined ? baseOptions.headers : {};
package/package.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "openapi-types": "^12.1.3",
14
14
  "openapi-typescript": "^7.10.1",
15
15
  "path-to-regexp": "^8.3.0",
16
- "prettier": "^3.6.2",
16
+ "prettier": "^3.8.0",
17
17
  "typescript": "^5.9.3",
18
18
  "url-pattern": "^1.0.3",
19
19
  "yargs": "^18.0.0",
@@ -41,9 +41,9 @@
41
41
  "@types/jest": "^30.0.0",
42
42
  "@types/js-yaml": "^4.0.9",
43
43
  "@types/jsonpath": "^0.2.4",
44
- "@types/node": "^25.0.3",
44
+ "@types/node": "^25.0.9",
45
45
  "@types/yargs": "^17.0.35",
46
- "@vitest/coverage-v8": "^4.0.16",
46
+ "@vitest/coverage-v8": "^4.0.17",
47
47
  "cross-env": "^10.1.0",
48
48
  "eslint": "^9.39.2",
49
49
  "eslint-config-prettier": "^10.1.8",
@@ -51,8 +51,8 @@
51
51
  "lint-staged": "^16.2.7",
52
52
  "semantic-release": "^25.0.2",
53
53
  "ts-node": "^10.9.2",
54
- "typescript-eslint": "^8.51.0",
55
- "undici": "^7.16.0",
54
+ "typescript-eslint": "^8.53.0",
55
+ "undici": "^7.18.2",
56
56
  "vitest": "^4.0.14"
57
57
  },
58
58
  "homepage": "https://github.com/julienandreu/zod-codegen",
@@ -75,13 +75,11 @@
75
75
  "node": ">=18.0.0"
76
76
  },
77
77
  "overrides": {
78
- "npm": {
79
- "glob": "^11.1.0",
80
- "tar": "^7.5.2"
81
- },
82
- "glob": "^11.1.0",
83
- "tar": "^7.5.2",
84
- "yargs": "^18.0.0"
78
+ "tar": "^7.5.3",
79
+ "diff": "^8.0.3",
80
+ "@actions/http-client": {
81
+ "undici": "^7.18.0"
82
+ }
85
83
  },
86
84
  "scripts": {
87
85
  "audit:fix": "npm audit fix",
@@ -107,5 +105,5 @@
107
105
  "release": "semantic-release",
108
106
  "release:dry": "semantic-release --dry-run"
109
107
  },
110
- "version": "1.6.1"
108
+ "version": "1.6.3"
111
109
  }