zod-codegen 1.0.1 → 1.0.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.
@@ -21,17 +21,17 @@ export class TypeScriptCodeGeneratorService {
21
21
  const safeCategorySchema = SchemaProperties.safeParse(schema);
22
22
  if (safeCategorySchema.success) {
23
23
  const safeCategory = safeCategorySchema.data;
24
- if (safeCategory.anyOf && Array.isArray(safeCategory.anyOf) && safeCategory.anyOf.length > 0) {
25
- return this.handleLogicalOperator('anyOf', safeCategory.anyOf, required);
24
+ if (safeCategory['anyOf'] && Array.isArray(safeCategory['anyOf']) && safeCategory['anyOf'].length > 0) {
25
+ return this.handleLogicalOperator('anyOf', safeCategory['anyOf'], required);
26
26
  }
27
- if (safeCategory.oneOf && Array.isArray(safeCategory.oneOf) && safeCategory.oneOf.length > 0) {
28
- return this.handleLogicalOperator('oneOf', safeCategory.oneOf, required);
27
+ if (safeCategory['oneOf'] && Array.isArray(safeCategory['oneOf']) && safeCategory['oneOf'].length > 0) {
28
+ return this.handleLogicalOperator('oneOf', safeCategory['oneOf'], required);
29
29
  }
30
- if (safeCategory.allOf && Array.isArray(safeCategory.allOf) && safeCategory.allOf.length > 0) {
31
- return this.handleLogicalOperator('allOf', safeCategory.allOf, required);
30
+ if (safeCategory['allOf'] && Array.isArray(safeCategory['allOf']) && safeCategory['allOf'].length > 0) {
31
+ return this.handleLogicalOperator('allOf', safeCategory['allOf'], required);
32
32
  }
33
- if (safeCategory.not) {
34
- return this.handleLogicalOperator('not', [safeCategory.not], required);
33
+ if (safeCategory['not']) {
34
+ return this.handleLogicalOperator('not', [safeCategory['not']], required);
35
35
  }
36
36
  return this.buildProperty(safeCategory, required);
37
37
  }
@@ -90,33 +90,103 @@ export class TypeScriptCodeGeneratorService {
90
90
  return ts.factory.createMethodDeclaration([ts.factory.createToken(ts.SyntaxKind.AsyncKeyword)], undefined, ts.factory.createPrivateIdentifier('#makeRequest'), undefined, [this.typeBuilder.createGenericType('T')], [
91
91
  this.typeBuilder.createParameter('method', 'string'),
92
92
  this.typeBuilder.createParameter('path', 'string'),
93
- this.typeBuilder.createParameter('options', 'unknown', ts.factory.createObjectLiteralExpression([], false), true),
93
+ this.typeBuilder.createParameter('options', '{params?: Record<string, string | number | boolean>; data?: unknown; contentType?: string}', ts.factory.createObjectLiteralExpression([], false), false),
94
94
  ], ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
95
95
  ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('T'), undefined),
96
96
  ]), ts.factory.createBlock([
97
+ // Build URL with query parameters
97
98
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
98
- ts.factory.createVariableDeclaration(ts.factory.createIdentifier('url'), undefined, undefined, ts.factory.createTemplateExpression(ts.factory.createTemplateHead('', ''), [
99
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier('baseUrl'), undefined, undefined, ts.factory.createTemplateExpression(ts.factory.createTemplateHead('', ''), [
99
100
  ts.factory.createTemplateSpan(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#baseUrl')), ts.factory.createTemplateMiddle('', '')),
100
101
  ts.factory.createTemplateSpan(ts.factory.createIdentifier('path'), ts.factory.createTemplateTail('', '')),
101
102
  ])),
102
103
  ], ts.NodeFlags.Const)),
104
+ ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
105
+ 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, [
106
+ ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params')),
107
+ ]), ts.factory.createIdentifier('length')), ts.factory.createToken(ts.SyntaxKind.GreaterThanToken), ts.factory.createNumericLiteral('0'))), undefined, (() => {
108
+ const urlObj = ts.factory.createNewExpression(ts.factory.createIdentifier('URL'), undefined, [
109
+ ts.factory.createIdentifier('baseUrl'),
110
+ ]);
111
+ const forEachCall = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('entries')), undefined, [
112
+ ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('params')),
113
+ ]), ts.factory.createIdentifier('forEach')), undefined, [
114
+ ts.factory.createArrowFunction(undefined, undefined, [
115
+ ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createArrayBindingPattern([
116
+ ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('key'), undefined),
117
+ ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('value'), undefined),
118
+ ]), undefined, undefined),
119
+ ], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
120
+ ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createPropertyAccessExpression(urlObj, ts.factory.createIdentifier('searchParams')), ts.factory.createIdentifier('set')), undefined, [
121
+ ts.factory.createIdentifier('key'),
122
+ ts.factory.createCallExpression(ts.factory.createIdentifier('String'), undefined, [ts.factory.createIdentifier('value')]),
123
+ ])),
124
+ ], false)),
125
+ ]);
126
+ // Use IIFE to execute forEach and return URL string
127
+ return ts.factory.createCallExpression(ts.factory.createParenthesizedExpression(ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
128
+ ts.factory.createExpressionStatement(forEachCall),
129
+ ts.factory.createReturnStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(urlObj, ts.factory.createIdentifier('toString')), undefined, [])),
130
+ ], false))), undefined, []);
131
+ })(), undefined, ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createNewExpression(ts.factory.createIdentifier('URL'), undefined, [
132
+ ts.factory.createIdentifier('baseUrl'),
133
+ ]), ts.factory.createIdentifier('toString')), undefined, []))),
134
+ ], ts.NodeFlags.Const)),
135
+ // Build headers with dynamic Content-Type
136
+ ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
137
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier('headers'), undefined, undefined, ts.factory.createObjectLiteralExpression([
138
+ ts.factory.createPropertyAssignment(ts.factory.createStringLiteral('Content-Type', true), ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('contentType')), ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.factory.createStringLiteral('application/x-www-form-urlencoded', true)), undefined, ts.factory.createStringLiteral('application/x-www-form-urlencoded', true), undefined, ts.factory.createStringLiteral('application/json', true))),
139
+ ], true)),
140
+ ], ts.NodeFlags.Const)),
141
+ // Build body with form-urlencoded support
142
+ ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
143
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier('body'), undefined, undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('data')), ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken), ts.factory.createIdentifier('undefined')), undefined, ts.factory.createConditionalExpression(ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('contentType')), ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.factory.createStringLiteral('application/x-www-form-urlencoded', true)), undefined,
144
+ // Form-urlencoded: convert object to URLSearchParams
145
+ ts.factory.createCallExpression(ts.factory.createParenthesizedExpression(ts.factory.createArrowFunction(undefined, undefined, [], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
146
+ ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
147
+ ts.factory.createVariableDeclaration(ts.factory.createIdentifier('params'), undefined, undefined, ts.factory.createNewExpression(ts.factory.createIdentifier('URLSearchParams'), undefined, [])),
148
+ ], ts.NodeFlags.Const)),
149
+ ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('entries')), undefined, [
150
+ ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('data')),
151
+ ]), ts.factory.createIdentifier('forEach')), undefined, [
152
+ ts.factory.createArrowFunction(undefined, undefined, [
153
+ ts.factory.createParameterDeclaration(undefined, undefined, ts.factory.createArrayBindingPattern([
154
+ ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('key'), undefined),
155
+ ts.factory.createBindingElement(undefined, undefined, ts.factory.createIdentifier('value'), undefined),
156
+ ]), undefined, undefined),
157
+ ], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
158
+ ts.factory.createExpressionStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('params'), ts.factory.createIdentifier('set')), undefined, [
159
+ ts.factory.createIdentifier('key'),
160
+ ts.factory.createCallExpression(ts.factory.createIdentifier('String'), undefined, [ts.factory.createIdentifier('value')]),
161
+ ])),
162
+ ], false)),
163
+ ])),
164
+ ts.factory.createReturnStatement(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('params'), ts.factory.createIdentifier('toString')), undefined, [])),
165
+ ], false))), undefined, []), undefined,
166
+ // JSON: stringify the data
167
+ ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('JSON'), ts.factory.createIdentifier('stringify')), undefined, [
168
+ ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('options'), ts.factory.createIdentifier('data')),
169
+ ])), undefined, ts.factory.createNull())),
170
+ ], ts.NodeFlags.Const)),
171
+ // Make fetch request
103
172
  ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
104
173
  ts.factory.createVariableDeclaration(ts.factory.createIdentifier('response'), undefined, undefined, ts.factory.createAwaitExpression(ts.factory.createCallExpression(ts.factory.createIdentifier('fetch'), undefined, [
105
174
  ts.factory.createIdentifier('url'),
106
175
  ts.factory.createObjectLiteralExpression([
107
176
  ts.factory.createShorthandPropertyAssignment(ts.factory.createIdentifier('method'), undefined),
108
- ts.factory.createPropertyAssignment(ts.factory.createIdentifier('headers'), ts.factory.createObjectLiteralExpression([
109
- ts.factory.createPropertyAssignment(ts.factory.createStringLiteral('Content-Type', true), ts.factory.createStringLiteral('application/json', true)),
110
- ], true)),
177
+ ts.factory.createPropertyAssignment(ts.factory.createIdentifier('headers'), ts.factory.createIdentifier('headers')),
178
+ ts.factory.createPropertyAssignment(ts.factory.createIdentifier('body'), ts.factory.createIdentifier('body')),
111
179
  ], true),
112
180
  ]))),
113
181
  ], ts.NodeFlags.Const)),
182
+ // Check response status
114
183
  ts.factory.createIfStatement(ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.ExclamationToken, ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('ok'))), ts.factory.createThrowStatement(ts.factory.createNewExpression(ts.factory.createIdentifier('Error'), undefined, [
115
184
  ts.factory.createTemplateExpression(ts.factory.createTemplateHead('HTTP ', 'HTTP '), [
116
185
  ts.factory.createTemplateSpan(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('status')), ts.factory.createTemplateMiddle(': ', ': ')),
117
186
  ts.factory.createTemplateSpan(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('statusText')), ts.factory.createTemplateTail('', '')),
118
187
  ]),
119
188
  ])), undefined),
189
+ // Return parsed JSON
120
190
  ts.factory.createReturnStatement(ts.factory.createAwaitExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('json')), undefined, []))),
121
191
  ], true));
122
192
  }
@@ -136,36 +206,260 @@ export class TypeScriptCodeGeneratorService {
136
206
  }, []);
137
207
  }
138
208
  buildEndpointMethod(method, path, schema, schemas) {
139
- const parameters = this.buildMethodParameters(schema, schemas);
209
+ const { parameters, pathParams, queryParams, hasRequestBody, contentType } = this.buildMethodParameters(schema, schemas);
140
210
  const responseType = this.getResponseType(schema, schemas);
141
- return ts.factory.createMethodDeclaration([ts.factory.createToken(ts.SyntaxKind.AsyncKeyword)], undefined, ts.factory.createIdentifier(String(schema.operationId)), undefined, undefined, parameters, responseType, ts.factory.createBlock([
142
- ts.factory.createReturnStatement(ts.factory.createAwaitExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#makeRequest')), undefined, [
143
- ts.factory.createStringLiteral(method.toUpperCase(), true),
144
- ts.factory.createStringLiteral(path, true),
145
- ]))),
146
- ], true));
211
+ const responseSchema = this.getResponseSchema(schema, schemas);
212
+ const statements = [];
213
+ // Build path with parameter substitution
214
+ const pathExpression = this.buildPathExpression(path, pathParams);
215
+ // Build query parameters object
216
+ const queryParamsExpression = queryParams.length > 0
217
+ ? ts.factory.createObjectLiteralExpression(queryParams.map((param) => {
218
+ const paramName = this.typeBuilder.sanitizeIdentifier(param.name);
219
+ return ts.factory.createPropertyAssignment(ts.factory.createStringLiteral(param.name, true), ts.factory.createIdentifier(paramName));
220
+ }), false)
221
+ : undefined;
222
+ // Build request body
223
+ const requestBodyExpression = hasRequestBody
224
+ ? ts.factory.createIdentifier('body')
225
+ : undefined;
226
+ // Build options object for makeRequest
227
+ const optionsProps = [];
228
+ if (queryParamsExpression) {
229
+ optionsProps.push(ts.factory.createPropertyAssignment(ts.factory.createIdentifier('params'), queryParamsExpression));
230
+ }
231
+ if (requestBodyExpression) {
232
+ optionsProps.push(ts.factory.createPropertyAssignment(ts.factory.createIdentifier('data'), requestBodyExpression));
233
+ }
234
+ // Add content type if it's form-urlencoded
235
+ if (hasRequestBody && contentType === 'application/x-www-form-urlencoded') {
236
+ optionsProps.push(ts.factory.createPropertyAssignment(ts.factory.createIdentifier('contentType'), ts.factory.createStringLiteral('application/x-www-form-urlencoded', true)));
237
+ }
238
+ const optionsExpression = ts.factory.createObjectLiteralExpression(optionsProps, false);
239
+ // Call makeRequest
240
+ const makeRequestCall = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createPrivateIdentifier('#makeRequest')), undefined, [ts.factory.createStringLiteral(method.toUpperCase(), true), pathExpression, optionsExpression]);
241
+ // Add Zod validation if we have a response schema
242
+ if (responseSchema) {
243
+ const validateCall = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(responseSchema, ts.factory.createIdentifier('parse')), undefined, [ts.factory.createAwaitExpression(makeRequestCall)]);
244
+ statements.push(ts.factory.createReturnStatement(validateCall));
245
+ }
246
+ else {
247
+ statements.push(ts.factory.createReturnStatement(ts.factory.createAwaitExpression(makeRequestCall)));
248
+ }
249
+ return ts.factory.createMethodDeclaration([ts.factory.createToken(ts.SyntaxKind.AsyncKeyword)], undefined, ts.factory.createIdentifier(String(schema.operationId)), undefined, undefined, parameters, responseType, ts.factory.createBlock(statements, true));
250
+ }
251
+ buildPathExpression(path, pathParams) {
252
+ // Replace {param} with ${param} for template literal
253
+ const pathParamNames = new Set(pathParams.map((p) => p.name));
254
+ const pathParamRegex = /\{([^}]+)\}/g;
255
+ const matches = [];
256
+ // Find all path parameters
257
+ for (const match of path.matchAll(pathParamRegex)) {
258
+ const paramName = match[1];
259
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
260
+ if (paramName && pathParamNames.has(paramName) && match.index !== undefined) {
261
+ matches.push({
262
+ index: match.index,
263
+ length: match[0].length,
264
+ name: paramName,
265
+ });
266
+ }
267
+ }
268
+ if (matches.length === 0) {
269
+ // No path parameters, return as string literal
270
+ return ts.factory.createStringLiteral(path, true);
271
+ }
272
+ // Build template expression
273
+ const templateSpans = [];
274
+ let lastIndex = 0;
275
+ for (const [index, m] of matches.entries()) {
276
+ const before = path.substring(lastIndex, m.index);
277
+ const sanitizedName = this.typeBuilder.sanitizeIdentifier(m.name);
278
+ const isLast = index === matches.length - 1;
279
+ const after = isLast ? path.substring(m.index + m.length) : '';
280
+ if (isLast) {
281
+ templateSpans.push(ts.factory.createTemplateSpan(ts.factory.createIdentifier(sanitizedName), ts.factory.createTemplateTail(after, after)));
282
+ }
283
+ else {
284
+ templateSpans.push(ts.factory.createTemplateSpan(ts.factory.createIdentifier(sanitizedName), ts.factory.createTemplateMiddle(before, before)));
285
+ }
286
+ lastIndex = m.index + m.length;
287
+ }
288
+ const firstMatch = matches[0];
289
+ if (!firstMatch) {
290
+ return ts.factory.createStringLiteral(path, true);
291
+ }
292
+ const head = path.substring(0, firstMatch.index);
293
+ return ts.factory.createTemplateExpression(ts.factory.createTemplateHead(head, head), templateSpans);
147
294
  }
148
295
  buildMethodParameters(schema, schemas) {
149
- void schemas; // Mark as intentionally unused
150
296
  const parameters = [];
297
+ const pathParams = [];
298
+ const queryParams = [];
299
+ // Extract path and query parameters
151
300
  if (schema.parameters) {
152
- schema.parameters.forEach((param) => {
153
- if (param.in === 'path' && param.required) {
154
- parameters.push(this.typeBuilder.createParameter(this.typeBuilder.sanitizeIdentifier(param.name), 'string', undefined, false));
301
+ for (const param of schema.parameters) {
302
+ const paramName = this.typeBuilder.sanitizeIdentifier(param.name);
303
+ const paramType = this.getParameterType(param.schema);
304
+ if (param.in === 'path') {
305
+ pathParams.push({ name: param.name, type: paramType });
306
+ parameters.push(this.typeBuilder.createParameter(paramName, paramType, undefined, false));
155
307
  }
156
- });
308
+ else if (param.in === 'query') {
309
+ // Improve type inference for query parameters
310
+ const queryParamType =
311
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
312
+ typeof param.schema === 'object' && param.schema !== null
313
+ ? (() => {
314
+ const paramSchema = param.schema;
315
+ // eslint-disable-next-line @typescript-eslint/dot-notation
316
+ if (paramSchema['type'] === 'array' && paramSchema['items']) {
317
+ // eslint-disable-next-line @typescript-eslint/dot-notation
318
+ const itemSchema = paramSchema['items'];
319
+ // eslint-disable-next-line @typescript-eslint/dot-notation
320
+ if (itemSchema['type'] === 'string') {
321
+ return 'string[]';
322
+ }
323
+ // eslint-disable-next-line @typescript-eslint/dot-notation
324
+ if (itemSchema['type'] === 'number' || itemSchema['type'] === 'integer') {
325
+ return 'number[]';
326
+ }
327
+ }
328
+ return paramType;
329
+ })()
330
+ : paramType;
331
+ queryParams.push({ name: param.name, type: queryParamType, required: param.required ?? false });
332
+ parameters.push(this.typeBuilder.createParameter(paramName, queryParamType, undefined, !param.required));
333
+ }
334
+ }
157
335
  }
158
- parameters.push(this.typeBuilder.createParameter('_', 'unknown', undefined, true));
159
- return parameters;
336
+ // Add request body parameter if present
337
+ // Check for both application/json and application/x-www-form-urlencoded
338
+ const jsonContent = schema.requestBody?.content?.['application/json'];
339
+ const formContent = schema.requestBody?.content?.['application/x-www-form-urlencoded'];
340
+ const hasRequestBody = !!(jsonContent ?? formContent);
341
+ if (hasRequestBody) {
342
+ const requestBodyContent = jsonContent ?? formContent;
343
+ const requestBodySchema = requestBodyContent?.schema;
344
+ const bodyType = typeof requestBodySchema === 'object' && requestBodySchema !== null
345
+ ? (() => {
346
+ const schemaObj = requestBodySchema;
347
+ const ref = schemaObj['$ref'];
348
+ if (ref && typeof ref === 'string' && ref.startsWith('#/components/schemas/')) {
349
+ const refName = ref.split('/').pop() ?? 'unknown';
350
+ return this.typeBuilder.sanitizeIdentifier(refName);
351
+ }
352
+ // Fallback to getSchemaTypeName for non-ref schemas
353
+ return this.getSchemaTypeName(requestBodySchema, schemas);
354
+ })()
355
+ : 'unknown';
356
+ parameters.push(this.typeBuilder.createParameter('body', bodyType, undefined, !schema.requestBody?.required));
357
+ }
358
+ // Determine content type for request body
359
+ const contentType = hasRequestBody && schema.requestBody?.content?.['application/x-www-form-urlencoded']
360
+ ? 'application/x-www-form-urlencoded'
361
+ : 'application/json';
362
+ return { parameters, pathParams, queryParams, hasRequestBody, contentType };
160
363
  }
161
- getResponseType(schema, schemas) {
162
- void schemas; // Mark as intentionally unused
364
+ getParameterType(schema) {
365
+ if (!schema || typeof schema !== 'object') {
366
+ return 'string';
367
+ }
368
+ const schemaObj = schema;
369
+ if (schemaObj.$ref) {
370
+ const refName = schemaObj.$ref.split('/').pop() ?? 'unknown';
371
+ return this.typeBuilder.sanitizeIdentifier(refName);
372
+ }
373
+ if (schemaObj.type === 'array' && schemaObj.items) {
374
+ const itemType = this.getParameterType(schemaObj.items);
375
+ return `${itemType}[]`;
376
+ }
377
+ switch (schemaObj.type) {
378
+ case 'integer':
379
+ case 'number':
380
+ return 'number';
381
+ case 'boolean':
382
+ return 'boolean';
383
+ case 'array':
384
+ return 'unknown[]';
385
+ case 'object':
386
+ return 'Record<string, unknown>';
387
+ case 'string':
388
+ // If it has enum values, we could generate a union type, but for simplicity, keep as string
389
+ // The Zod schema will handle the validation
390
+ return 'string';
391
+ default:
392
+ return 'string';
393
+ }
394
+ }
395
+ getSchemaTypeName(schema, _schemas) {
396
+ if (typeof schema !== 'object' || schema === null) {
397
+ return 'unknown';
398
+ }
399
+ const schemaObj = schema;
400
+ // Check for $ref using both dot notation and bracket notation
401
+ // eslint-disable-next-line @typescript-eslint/dot-notation
402
+ const ref = schemaObj['$ref'];
403
+ if (ref && typeof ref === 'string') {
404
+ const refName = ref.split('/').pop() ?? 'unknown';
405
+ return this.typeBuilder.sanitizeIdentifier(refName);
406
+ }
407
+ if (schemaObj.type === 'array' && schemaObj.items) {
408
+ const itemType = this.getSchemaTypeName(schemaObj.items, _schemas);
409
+ return `${itemType}[]`;
410
+ }
411
+ switch (schemaObj.type) {
412
+ case 'integer':
413
+ case 'number':
414
+ return 'number';
415
+ case 'boolean':
416
+ return 'boolean';
417
+ case 'string':
418
+ return 'string';
419
+ case 'object':
420
+ return 'Record<string, unknown>';
421
+ default:
422
+ return 'unknown';
423
+ }
424
+ }
425
+ getResponseSchema(schema, _schemas) {
426
+ // Try to find a 200 response first, then 201, then default
163
427
  const response200 = schema.responses?.['200'];
164
- if (!response200?.content?.['application/json']?.schema) {
428
+ const response201 = schema.responses?.['201'];
429
+ const responseDefault = schema.responses?.['default'];
430
+ const response = response200 ?? response201 ?? responseDefault;
431
+ if (!response?.content?.['application/json']?.schema) {
165
432
  return undefined;
166
433
  }
434
+ const responseSchema = response.content['application/json'].schema;
435
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
436
+ if (responseSchema !== null && typeof responseSchema === 'object' && '$ref' in responseSchema) {
437
+ // eslint-disable-next-line @typescript-eslint/dot-notation
438
+ const ref = responseSchema['$ref'];
439
+ if (typeof ref === 'string') {
440
+ const refName = ref.split('/').pop() ?? 'unknown';
441
+ return ts.factory.createIdentifier(this.typeBuilder.sanitizeIdentifier(refName));
442
+ }
443
+ }
444
+ // For inline schemas, we'd need to generate a schema, but for now return undefined
445
+ // This could be enhanced to generate inline schemas
446
+ return undefined;
447
+ }
448
+ getResponseType(schema, schemas) {
449
+ // Try to find a 200 response first, then 201, then default
450
+ const response200 = schema.responses?.['200'];
451
+ const response201 = schema.responses?.['201'];
452
+ const responseDefault = schema.responses?.['default'];
453
+ const response = response200 ?? response201 ?? responseDefault;
454
+ if (!response?.content?.['application/json']?.schema) {
455
+ return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
456
+ ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword),
457
+ ]);
458
+ }
459
+ const responseSchema = response.content['application/json'].schema;
460
+ const typeName = this.getSchemaTypeName(responseSchema, schemas);
167
461
  return ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
168
- ts.factory.createKeywordTypeNode(ts.SyntaxKind.UnknownKeyword),
462
+ ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(typeName), undefined),
169
463
  ]);
170
464
  }
171
465
  buildBaseUrlConstant(openapi) {
@@ -210,35 +504,73 @@ export class TypeScriptCodeGeneratorService {
210
504
  }
211
505
  const prop = safeProperty.data;
212
506
  if (this.isReference(prop)) {
213
- return this.buildFromReference(prop);
507
+ const refSchema = this.buildFromReference(prop);
508
+ return required
509
+ ? refSchema
510
+ : ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(refSchema, ts.factory.createIdentifier('optional')), undefined, []);
214
511
  }
215
- const methodsToApply = [];
216
- if (prop.anyOf && Array.isArray(prop.anyOf) && prop.anyOf.length > 0) {
217
- return this.handleLogicalOperator('anyOf', prop.anyOf, required);
512
+ if (prop['anyOf'] && Array.isArray(prop['anyOf']) && prop['anyOf'].length > 0) {
513
+ return this.handleLogicalOperator('anyOf', prop['anyOf'], required);
218
514
  }
219
- if (prop.oneOf && Array.isArray(prop.oneOf) && prop.oneOf.length > 0) {
220
- return this.handleLogicalOperator('oneOf', prop.oneOf, required);
515
+ if (prop['oneOf'] && Array.isArray(prop['oneOf']) && prop['oneOf'].length > 0) {
516
+ return this.handleLogicalOperator('oneOf', prop['oneOf'], required);
221
517
  }
222
- if (prop.allOf && Array.isArray(prop.allOf) && prop.allOf.length > 0) {
223
- return this.handleLogicalOperator('allOf', prop.allOf, required);
518
+ if (prop['allOf'] && Array.isArray(prop['allOf']) && prop['allOf'].length > 0) {
519
+ return this.handleLogicalOperator('allOf', prop['allOf'], required);
224
520
  }
225
- if (prop.not) {
226
- return this.handleLogicalOperator('not', [prop.not], required);
521
+ if (prop['not']) {
522
+ return this.handleLogicalOperator('not', [prop['not']], required);
227
523
  }
228
- switch (prop.type) {
229
- case 'array':
230
- return this.buildZodAST([
524
+ // Handle enum
525
+ if (prop['enum'] && Array.isArray(prop['enum']) && prop['enum'].length > 0) {
526
+ const enumValues = prop['enum'].map((val) => {
527
+ if (typeof val === 'string') {
528
+ return ts.factory.createStringLiteral(val, true);
529
+ }
530
+ if (typeof val === 'number') {
531
+ // Handle negative numbers correctly
532
+ if (val < 0) {
533
+ return ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.MinusToken, ts.factory.createNumericLiteral(String(Math.abs(val))));
534
+ }
535
+ return ts.factory.createNumericLiteral(String(val));
536
+ }
537
+ if (typeof val === 'boolean') {
538
+ return val ? ts.factory.createTrue() : ts.factory.createFalse();
539
+ }
540
+ return ts.factory.createStringLiteral(String(val), true);
541
+ });
542
+ const enumExpression = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('z'), ts.factory.createIdentifier('enum')), undefined, [ts.factory.createArrayLiteralExpression(enumValues, false)]);
543
+ return required
544
+ ? enumExpression
545
+ : ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(enumExpression, ts.factory.createIdentifier('optional')), undefined, []);
546
+ }
547
+ switch (prop['type']) {
548
+ case 'array': {
549
+ const itemsSchema = prop['items'] ? this.buildProperty(prop['items'], true) : this.buildZodAST(['unknown']);
550
+ let arraySchema = this.buildZodAST([
231
551
  {
232
552
  type: 'array',
233
- args: prop.items ? [this.buildProperty(prop.items, true)] : [],
553
+ args: [itemsSchema],
234
554
  },
235
- ...(!required ? ['optional'] : []),
236
555
  ]);
556
+ // Apply array constraints
557
+ if (typeof prop['minItems'] === 'number') {
558
+ arraySchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(arraySchema, ts.factory.createIdentifier('min')), undefined, [ts.factory.createNumericLiteral(String(prop['minItems']))]);
559
+ }
560
+ if (typeof prop['maxItems'] === 'number') {
561
+ arraySchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(arraySchema, ts.factory.createIdentifier('max')), undefined, [ts.factory.createNumericLiteral(String(prop['maxItems']))]);
562
+ }
563
+ return required
564
+ ? arraySchema
565
+ : ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(arraySchema, ts.factory.createIdentifier('optional')), undefined, []);
566
+ }
237
567
  case 'object': {
238
- const { properties = {}, required: propRequired = [] } = prop;
568
+ const propObj = prop;
569
+ const properties = (propObj['properties'] ?? {});
570
+ const propRequired = (propObj['required'] ?? []);
239
571
  const propertiesEntries = Object.entries(properties);
240
572
  if (propertiesEntries.length > 0) {
241
- return this.buildZodAST([
573
+ const objectSchema = this.buildZodAST([
242
574
  {
243
575
  type: 'object',
244
576
  args: [
@@ -247,8 +579,28 @@ export class TypeScriptCodeGeneratorService {
247
579
  }), true),
248
580
  ],
249
581
  },
250
- ...(!required ? ['optional'] : []),
251
582
  ]);
583
+ // Apply object constraints
584
+ let constrainedSchema = objectSchema;
585
+ if (typeof prop['minProperties'] === 'number') {
586
+ constrainedSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(constrainedSchema, ts.factory.createIdentifier('refine')), undefined, [
587
+ ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'obj', undefined, undefined, undefined)], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('keys')), undefined, [ts.factory.createIdentifier('obj')]), ts.factory.createIdentifier('length')), ts.factory.createToken(ts.SyntaxKind.GreaterThanEqualsToken), ts.factory.createNumericLiteral(String(prop['minProperties'])))),
588
+ ts.factory.createObjectLiteralExpression([
589
+ ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Object must have at least ${String(prop['minProperties'])} properties`)),
590
+ ]),
591
+ ]);
592
+ }
593
+ if (typeof prop['maxProperties'] === 'number') {
594
+ constrainedSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(constrainedSchema, ts.factory.createIdentifier('refine')), undefined, [
595
+ ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'obj', undefined, undefined, undefined)], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('keys')), undefined, [ts.factory.createIdentifier('obj')]), ts.factory.createIdentifier('length')), ts.factory.createToken(ts.SyntaxKind.LessThanEqualsToken), ts.factory.createNumericLiteral(String(prop['maxProperties'])))),
596
+ ts.factory.createObjectLiteralExpression([
597
+ ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Object must have at most ${String(prop['maxProperties'])} properties`)),
598
+ ]),
599
+ ]);
600
+ }
601
+ return required
602
+ ? constrainedSchema
603
+ : ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(constrainedSchema, ts.factory.createIdentifier('optional')), undefined, []);
252
604
  }
253
605
  return this.buildZodAST([
254
606
  {
@@ -257,20 +609,158 @@ export class TypeScriptCodeGeneratorService {
257
609
  },
258
610
  ]);
259
611
  }
260
- case 'integer':
261
- methodsToApply.push('int');
262
- return this.buildZodAST(['number', ...methodsToApply, ...(!required ? ['optional'] : [])]);
263
- case 'number':
264
- return this.buildZodAST(['number', ...(!required ? ['optional'] : [])]);
265
- case 'string':
266
- return this.buildZodAST(['string', ...(!required ? ['optional'] : [])]);
267
- case 'boolean':
268
- return this.buildZodAST(['boolean', ...(!required ? ['optional'] : [])]);
612
+ case 'integer': {
613
+ let numberSchema = this.buildZodAST(['number', 'int']);
614
+ // Apply number constraints
615
+ if (prop['minimum'] !== undefined && typeof prop['minimum'] === 'number') {
616
+ const minValue = prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean'
617
+ ? prop['minimum'] + 1
618
+ : prop['minimum'];
619
+ numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier(prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean' ? 'gt' : 'gte')), undefined, [ts.factory.createNumericLiteral(String(minValue))]);
620
+ }
621
+ if (prop['maximum'] !== undefined && typeof prop['maximum'] === 'number') {
622
+ const maxValue = prop['exclusiveMaximum'] && typeof prop['exclusiveMaximum'] === 'boolean'
623
+ ? prop['maximum'] - 1
624
+ : prop['maximum'];
625
+ numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier(prop['exclusiveMaximum'] ? 'lt' : 'lte')), undefined, [ts.factory.createNumericLiteral(String(maxValue))]);
626
+ }
627
+ if (typeof prop['multipleOf'] === 'number') {
628
+ const refineFunction = ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'val', undefined, undefined, undefined)], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createBinaryExpression(ts.factory.createIdentifier('val'), ts.factory.createToken(ts.SyntaxKind.PercentToken), ts.factory.createNumericLiteral(String(prop['multipleOf']))), ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.factory.createNumericLiteral('0')));
629
+ const refineOptions = ts.factory.createObjectLiteralExpression([
630
+ ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Number must be a multiple of ${String(prop['multipleOf'])}`)),
631
+ ]);
632
+ numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier('refine')), undefined, [refineFunction, refineOptions]);
633
+ }
634
+ return required
635
+ ? numberSchema
636
+ : ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier('optional')), undefined, []);
637
+ }
638
+ case 'number': {
639
+ let numberSchema = this.buildZodAST(['number']);
640
+ // Apply number constraints
641
+ if (prop['minimum'] !== undefined && typeof prop['minimum'] === 'number') {
642
+ const minValue = prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean'
643
+ ? prop['minimum'] + 1
644
+ : prop['minimum'];
645
+ numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier(prop['exclusiveMinimum'] && typeof prop['exclusiveMinimum'] === 'boolean' ? 'gt' : 'gte')), undefined, [ts.factory.createNumericLiteral(String(minValue))]);
646
+ }
647
+ if (prop['maximum'] !== undefined && typeof prop['maximum'] === 'number') {
648
+ const maxValue = prop['exclusiveMaximum'] && typeof prop['exclusiveMaximum'] === 'boolean'
649
+ ? prop['maximum'] - 1
650
+ : prop['maximum'];
651
+ numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier(prop['exclusiveMaximum'] ? 'lt' : 'lte')), undefined, [ts.factory.createNumericLiteral(String(maxValue))]);
652
+ }
653
+ if (typeof prop['multipleOf'] === 'number') {
654
+ const refineFunction = ts.factory.createArrowFunction(undefined, undefined, [ts.factory.createParameterDeclaration(undefined, undefined, 'val', undefined, undefined, undefined)], undefined, ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBinaryExpression(ts.factory.createBinaryExpression(ts.factory.createIdentifier('val'), ts.factory.createToken(ts.SyntaxKind.PercentToken), ts.factory.createNumericLiteral(String(prop['multipleOf']))), ts.factory.createToken(ts.SyntaxKind.EqualsEqualsEqualsToken), ts.factory.createNumericLiteral('0')));
655
+ const refineOptions = ts.factory.createObjectLiteralExpression([
656
+ ts.factory.createPropertyAssignment(ts.factory.createIdentifier('message'), ts.factory.createStringLiteral(`Number must be a multiple of ${String(prop['multipleOf'])}`)),
657
+ ]);
658
+ numberSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier('refine')), undefined, [refineFunction, refineOptions]);
659
+ }
660
+ return required
661
+ ? numberSchema
662
+ : ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(numberSchema, ts.factory.createIdentifier('optional')), undefined, []);
663
+ }
664
+ case 'string': {
665
+ let stringSchema = this.buildZodAST(['string']);
666
+ // Apply string format
667
+ if (prop['format']) {
668
+ switch (prop['format']) {
669
+ case 'email':
670
+ stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('email')), undefined, []);
671
+ break;
672
+ case 'uri':
673
+ case 'url':
674
+ stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('url')), undefined, []);
675
+ break;
676
+ case 'uuid':
677
+ stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('uuid')), undefined, []);
678
+ break;
679
+ case 'date-time':
680
+ stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('datetime')), undefined, []);
681
+ break;
682
+ case 'date':
683
+ stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('date')), undefined, []);
684
+ break;
685
+ case 'time':
686
+ stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('time')), undefined, []);
687
+ break;
688
+ // Add more formats as needed
689
+ }
690
+ }
691
+ // Apply string constraints
692
+ if (typeof prop['minLength'] === 'number') {
693
+ stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('min')), undefined, [ts.factory.createNumericLiteral(String(prop['minLength']))]);
694
+ }
695
+ if (typeof prop['maxLength'] === 'number') {
696
+ stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('max')), undefined, [ts.factory.createNumericLiteral(String(prop['maxLength']))]);
697
+ }
698
+ if (prop['pattern'] && typeof prop['pattern'] === 'string') {
699
+ stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('regex')), undefined, [
700
+ ts.factory.createNewExpression(ts.factory.createIdentifier('RegExp'), undefined, [
701
+ ts.factory.createStringLiteral(prop['pattern'], true),
702
+ ]),
703
+ ]);
704
+ }
705
+ // Apply default value if not required
706
+ if (!required && prop['default'] !== undefined) {
707
+ const defaultValue = this.buildDefaultValue(prop['default']);
708
+ stringSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('default')), undefined, [defaultValue]);
709
+ }
710
+ return required
711
+ ? stringSchema
712
+ : ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(stringSchema, ts.factory.createIdentifier('optional')), undefined, []);
713
+ }
714
+ case 'boolean': {
715
+ let booleanSchema = this.buildZodAST(['boolean']);
716
+ // Apply default value if not required
717
+ if (!required && prop['default'] !== undefined) {
718
+ const defaultValue = typeof prop['default'] === 'boolean'
719
+ ? prop['default']
720
+ ? ts.factory.createTrue()
721
+ : ts.factory.createFalse()
722
+ : ts.factory.createFalse();
723
+ booleanSchema = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(booleanSchema, ts.factory.createIdentifier('default')), undefined, [defaultValue]);
724
+ }
725
+ return required
726
+ ? booleanSchema
727
+ : ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(booleanSchema, ts.factory.createIdentifier('optional')), undefined, []);
728
+ }
269
729
  case 'unknown':
270
730
  default:
271
731
  return this.buildZodAST(['unknown', ...(!required ? ['optional'] : [])]);
272
732
  }
273
733
  }
734
+ buildDefaultValue(value) {
735
+ if (typeof value === 'string') {
736
+ return ts.factory.createStringLiteral(value, true);
737
+ }
738
+ if (typeof value === 'number') {
739
+ return ts.factory.createNumericLiteral(String(value));
740
+ }
741
+ if (typeof value === 'boolean') {
742
+ return value ? ts.factory.createTrue() : ts.factory.createFalse();
743
+ }
744
+ if (value === null) {
745
+ return ts.factory.createNull();
746
+ }
747
+ if (Array.isArray(value)) {
748
+ return ts.factory.createArrayLiteralExpression(value.map((item) => this.buildDefaultValue(item)), false);
749
+ }
750
+ if (typeof value === 'object') {
751
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
752
+ if (value === null) {
753
+ return ts.factory.createNull();
754
+ }
755
+ return ts.factory.createObjectLiteralExpression(Object.entries(value).map(([key, val]) => ts.factory.createPropertyAssignment(ts.factory.createIdentifier(key), this.buildDefaultValue(val))), true);
756
+ }
757
+ if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
758
+ return ts.factory.createStringLiteral(String(value), true);
759
+ }
760
+ // For objects and arrays, we need to handle them differently
761
+ // This should not happen in practice, but we handle it for type safety
762
+ return ts.factory.createStringLiteral(JSON.stringify(value), true);
763
+ }
274
764
  handleLogicalOperator(operator, schemas, required) {
275
765
  const logicalExpression = this.buildLogicalOperator(operator, schemas);
276
766
  return required
@@ -309,6 +799,7 @@ export class TypeScriptCodeGeneratorService {
309
799
  }
310
800
  buildSchemaFromLogicalOperator(schema) {
311
801
  if (this.isReference(schema)) {
802
+ // In logical operators, references are always required (they're part of a union/intersection)
312
803
  return this.buildFromReference(schema);
313
804
  }
314
805
  const safeSchema = SchemaProperties.safeParse(schema);