@cerios/openapi-to-zod 1.4.0 → 1.5.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.
package/dist/index.mjs CHANGED
@@ -1,193 +1,30 @@
1
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
- }) : x)(function(x) {
4
- if (typeof require !== "undefined") return require.apply(this, arguments);
5
- throw Error('Dynamic require of "' + x + '" is not supported');
6
- });
7
-
8
- // src/errors.ts
9
- var GeneratorError = class extends Error {
10
- constructor(message, code, context) {
11
- var _a;
12
- super(message);
13
- this.code = code;
14
- this.context = context;
15
- this.name = "GeneratorError";
16
- (_a = Error.captureStackTrace) == null ? void 0 : _a.call(Error, this, this.constructor);
17
- }
18
- };
19
- var SpecValidationError = class extends GeneratorError {
20
- constructor(message, context) {
21
- super(message, "SPEC_VALIDATION_ERROR", context);
22
- this.name = "SpecValidationError";
23
- }
24
- };
25
- var FileOperationError = class extends GeneratorError {
26
- constructor(message, filePath, context) {
27
- super(message, "FILE_OPERATION_ERROR", { ...context, filePath });
28
- this.filePath = filePath;
29
- this.name = "FileOperationError";
30
- }
31
- };
32
- var ConfigValidationError = class extends GeneratorError {
33
- constructor(message, configPath, context) {
34
- super(message, "CONFIG_VALIDATION_ERROR", { ...context, configPath });
35
- this.configPath = configPath;
36
- this.name = "ConfigValidationError";
37
- }
38
- };
39
- var SchemaGenerationError = class extends GeneratorError {
40
- constructor(message, schemaName, context) {
41
- super(message, "SCHEMA_GENERATION_ERROR", { ...context, schemaName });
42
- this.schemaName = schemaName;
43
- this.name = "SchemaGenerationError";
44
- }
45
- };
46
- var CircularReferenceError = class extends SchemaGenerationError {
47
- constructor(schemaName, referencePath) {
48
- const pathStr = referencePath.join(" -> ");
49
- super(`Circular reference detected in schema: ${pathStr}`, schemaName, { referencePath, circularPath: pathStr });
50
- this.referencePath = referencePath;
51
- this.name = "CircularReferenceError";
52
- }
53
- };
54
- var CliOptionsError = class extends GeneratorError {
55
- constructor(message, context) {
56
- super(message, "CLI_OPTIONS_ERROR", context);
57
- this.name = "CliOptionsError";
58
- }
59
- };
60
- var ConfigurationError = class extends GeneratorError {
61
- constructor(message, context) {
62
- super(message, "CONFIGURATION_ERROR", context);
63
- this.name = "ConfigurationError";
64
- }
65
- };
66
-
67
- // src/openapi-generator.ts
68
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
69
- import { dirname, normalize } from "path";
70
- import { minimatch as minimatch3 } from "minimatch";
71
- import { parse } from "yaml";
72
-
73
- // src/utils/name-utils.ts
74
- function sanitizeIdentifier(str) {
75
- return str.replace(/[^a-zA-Z0-9._\-\s]+/g, "_");
76
- }
77
- function toCamelCase(str, options) {
78
- const sanitized = sanitizeIdentifier(str);
79
- const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
80
- let name;
81
- if (words.length === 0) {
82
- name = str.charAt(0).toLowerCase() + str.slice(1);
83
- } else if (words.length === 1) {
84
- name = words[0].charAt(0).toLowerCase() + words[0].slice(1);
85
- } else {
86
- name = words[0].charAt(0).toLowerCase() + words[0].slice(1) + words.slice(1).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
87
- }
88
- if (options == null ? void 0 : options.prefix) {
89
- const prefix = options.prefix.charAt(0).toLowerCase() + options.prefix.slice(1);
90
- name = prefix + name.charAt(0).toUpperCase() + name.slice(1);
91
- }
92
- if (options == null ? void 0 : options.suffix) {
93
- const suffix = options.suffix.charAt(0).toUpperCase() + options.suffix.slice(1);
94
- name = name + suffix;
95
- }
96
- return name;
97
- }
98
- function toPascalCase(str) {
99
- const stringValue = String(str);
100
- const isAlreadyValidCase = /^[a-zA-Z][a-zA-Z0-9]*$/.test(stringValue);
101
- if (isAlreadyValidCase) {
102
- return stringValue.charAt(0).toUpperCase() + stringValue.slice(1);
103
- }
104
- const sanitized = sanitizeIdentifier(stringValue);
105
- const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
106
- let result;
107
- if (words.length === 0) {
108
- result = "Value";
109
- } else {
110
- result = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
111
- }
112
- if (/^\d/.test(result)) {
113
- result = `N${result}`;
114
- }
115
- if (!result || /^_+$/.test(result)) {
116
- return "Value";
117
- }
118
- return result;
119
- }
120
- function resolveRef(ref) {
121
- const parts = ref.split("/");
122
- return parts[parts.length - 1];
123
- }
1
+ // src/index.ts
2
+ import {
3
+ CircularReferenceError,
4
+ CliOptionsError,
5
+ ConfigValidationError,
6
+ FileOperationError,
7
+ GeneratorError,
8
+ SchemaGenerationError as SchemaGenerationError2,
9
+ SpecValidationError as SpecValidationError2
10
+ } from "@cerios/openapi-core";
124
11
 
125
- // src/generators/enum-generator.ts
126
- function generateEnum(name, values, options) {
127
- const schemaName = `${toCamelCase(name, options)}Schema`;
128
- const typeName = toPascalCase(name);
129
- const allBooleans = values.every((v) => typeof v === "boolean");
130
- if (allBooleans) {
131
- const schemaCode2 = `export const ${schemaName} = z.boolean();`;
132
- const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
133
- return { schemaCode: schemaCode2, typeCode: typeCode2 };
134
- }
135
- const allStrings = values.every((v) => typeof v === "string");
136
- if (allStrings) {
137
- const enumValues = values.map((v) => `"${v}"`).join(", ");
138
- const schemaCode2 = `export const ${schemaName} = z.enum([${enumValues}]);`;
139
- const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
140
- return { schemaCode: schemaCode2, typeCode: typeCode2 };
141
- }
142
- const literalValues = values.map((v) => {
143
- if (typeof v === "string") {
144
- return `z.literal("${v}")`;
145
- }
146
- return `z.literal(${v})`;
147
- }).join(", ");
148
- const schemaCode = `export const ${schemaName} = z.union([${literalValues}]);`;
149
- const typeCode = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
150
- return { schemaCode, typeCode };
151
- }
12
+ // src/generators/property-generator.ts
13
+ import {
14
+ getPrimaryType,
15
+ hasMultipleTypes,
16
+ isNullable,
17
+ LRUCache,
18
+ resolveRefName,
19
+ stripPrefix,
20
+ toCamelCase,
21
+ toPascalCase
22
+ } from "@cerios/openapi-core";
152
23
 
153
24
  // src/utils/string-utils.ts
154
- function escapeDescription(str) {
155
- return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
156
- }
157
- function escapePattern(str) {
158
- return str.replace(/(?<!\\)\//g, "\\/");
159
- }
160
- function escapeJSDoc(str) {
161
- return str.replace(/\*\//g, "*\\/");
162
- }
163
- function wrapNullable(validation, isNullable2) {
164
- return isNullable2 ? `${validation}.nullable()` : validation;
165
- }
166
- function isNullable(schema, defaultNullable = false) {
167
- if (schema.nullable === true) {
168
- return true;
169
- }
170
- if (schema.nullable === false) {
171
- return false;
172
- }
173
- if (Array.isArray(schema.type)) {
174
- return schema.type.includes("null");
175
- }
176
- return defaultNullable;
177
- }
178
- function getPrimaryType(schema) {
179
- if (Array.isArray(schema.type)) {
180
- const nonNullType = schema.type.find((t) => t !== "null");
181
- return nonNullType;
182
- }
183
- return schema.type;
184
- }
185
- function hasMultipleTypes(schema) {
186
- if (Array.isArray(schema.type)) {
187
- const nonNullTypes = schema.type.filter((t) => t !== "null");
188
- return nonNullTypes.length > 1;
189
- }
190
- return false;
25
+ import { escapeDescription } from "@cerios/openapi-core";
26
+ function wrapNullable(validation, nullable) {
27
+ return nullable ? `${validation}.nullable()` : validation;
191
28
  }
192
29
  function addDescription(validation, description, useDescribe) {
193
30
  if (!description || !useDescribe) return validation;
@@ -195,167 +32,6 @@ function addDescription(validation, description, useDescribe) {
195
32
  return `${validation}.describe("${escapedDesc}")`;
196
33
  }
197
34
 
198
- // src/generators/jsdoc-generator.ts
199
- function generateJSDoc(schema, name, options = { includeDescriptions: true }) {
200
- if (!schema || typeof schema !== "object") {
201
- return "";
202
- }
203
- if (!options.includeDescriptions) {
204
- if (schema.deprecated) {
205
- return "/** @deprecated */\n";
206
- }
207
- return "";
208
- }
209
- if (!schema.description && !schema.title && !schema.deprecated && !schema.examples && schema.example === void 0) {
210
- return "";
211
- }
212
- const parts = [];
213
- if (schema.title && typeof schema.title === "string" && (!name || schema.title !== name)) {
214
- const sanitizedTitle = escapeJSDoc(schema.title).replace(/@/g, "\\@");
215
- parts.push(sanitizedTitle);
216
- }
217
- if (schema.description && typeof schema.description === "string") {
218
- const sanitizedDesc = escapeJSDoc(schema.description).replace(/@/g, "\\@").replace(/\*\//g, "*\\/");
219
- parts.push(sanitizedDesc);
220
- }
221
- if (schema.examples && Array.isArray(schema.examples) && schema.examples.length > 0) {
222
- try {
223
- const examplesStr = schema.examples.map((ex) => JSON.stringify(ex)).join(", ");
224
- parts.push(`@example ${examplesStr}`);
225
- } catch (error) {
226
- console.warn("Warning: Could not serialize schema examples", error);
227
- }
228
- } else if (schema.example !== void 0) {
229
- try {
230
- parts.push(`@example ${JSON.stringify(schema.example)}`);
231
- } catch (error) {
232
- console.warn("Warning: Could not serialize schema example", error);
233
- }
234
- }
235
- if (schema.deprecated) {
236
- parts.push("@deprecated");
237
- }
238
- if (parts.length === 0) {
239
- return "";
240
- }
241
- const fullComment = parts.join(" ");
242
- return `/** ${fullComment} */
243
- `;
244
- }
245
-
246
- // src/utils/lru-cache.ts
247
- var LRUCache = class {
248
- constructor(maxSize) {
249
- this.cache = /* @__PURE__ */ new Map();
250
- this.maxSize = maxSize;
251
- }
252
- get capacity() {
253
- return this.maxSize;
254
- }
255
- get(key) {
256
- if (!this.cache.has(key)) return void 0;
257
- const value = this.cache.get(key);
258
- if (value === void 0) return void 0;
259
- this.cache.delete(key);
260
- this.cache.set(key, value);
261
- return value;
262
- }
263
- set(key, value) {
264
- if (this.cache.has(key)) {
265
- this.cache.delete(key);
266
- } else if (this.cache.size >= this.maxSize) {
267
- const firstKey = this.cache.keys().next().value;
268
- if (firstKey !== void 0) {
269
- this.cache.delete(firstKey);
270
- }
271
- }
272
- this.cache.set(key, value);
273
- }
274
- has(key) {
275
- return this.cache.has(key);
276
- }
277
- clear() {
278
- this.cache.clear();
279
- }
280
- size() {
281
- return this.cache.size;
282
- }
283
- };
284
-
285
- // src/utils/pattern-utils.ts
286
- import { minimatch } from "minimatch";
287
- function isValidGlobPattern(pattern) {
288
- try {
289
- new minimatch.Minimatch(pattern);
290
- return true;
291
- } catch {
292
- return false;
293
- }
294
- }
295
- function isGlobPattern(pattern) {
296
- return /[*?[\]{}!]/.test(pattern);
297
- }
298
- function stripPrefix(input, pattern, ensureLeadingChar) {
299
- if (!pattern) {
300
- return input;
301
- }
302
- if (isGlobPattern(pattern) && !isValidGlobPattern(pattern)) {
303
- console.warn(`\u26A0\uFE0F Invalid glob pattern "${pattern}": Pattern is malformed`);
304
- return input;
305
- }
306
- if (isGlobPattern(pattern)) {
307
- let longestMatch = -1;
308
- for (let i = 1; i <= input.length; i++) {
309
- const testPrefix = input.substring(0, i);
310
- if (minimatch(testPrefix, pattern)) {
311
- longestMatch = i;
312
- }
313
- }
314
- if (longestMatch > 0) {
315
- const stripped = input.substring(longestMatch);
316
- if (ensureLeadingChar) {
317
- if (stripped === "") {
318
- return ensureLeadingChar;
319
- }
320
- if (!stripped.startsWith(ensureLeadingChar)) {
321
- return `${ensureLeadingChar}${stripped}`;
322
- }
323
- }
324
- return stripped === "" && !ensureLeadingChar ? input : stripped;
325
- }
326
- return input;
327
- }
328
- if (input.startsWith(pattern)) {
329
- const stripped = input.substring(pattern.length);
330
- if (ensureLeadingChar) {
331
- if (stripped === "") {
332
- return ensureLeadingChar;
333
- }
334
- if (!stripped.startsWith(ensureLeadingChar)) {
335
- return `${ensureLeadingChar}${stripped}`;
336
- }
337
- }
338
- return stripped;
339
- }
340
- return input;
341
- }
342
- function stripPathPrefix(path, pattern) {
343
- if (!pattern) {
344
- return path;
345
- }
346
- if (!isGlobPattern(pattern)) {
347
- let normalizedPattern = pattern.trim();
348
- if (!normalizedPattern.startsWith("/")) {
349
- normalizedPattern = `/${normalizedPattern}`;
350
- }
351
- if (normalizedPattern.endsWith("/") && normalizedPattern !== "/") {
352
- normalizedPattern = normalizedPattern.slice(0, -1);
353
- }
354
- return stripPrefix(path, normalizedPattern, "/");
355
- }
356
- return stripPrefix(path, pattern, "/");
357
- }
358
-
359
35
  // src/validators/array-validator.ts
360
36
  function generateArrayValidation(schema, context) {
361
37
  var _a;
@@ -523,15 +199,14 @@ function detectConflictingProperties(schemas, context) {
523
199
  function generateAllOf(schemas, isNullable2, context, currentSchema) {
524
200
  if (schemas.length === 1) {
525
201
  const singleSchema = context.generatePropertySchema(schemas[0], currentSchema, false, true);
526
- return wrapNullable(singleSchema, isNullable2);
202
+ return { schema: wrapNullable(singleSchema, isNullable2), conflicts: [] };
527
203
  }
528
204
  const conflicts = detectConflictingProperties(schemas, context);
529
- let conflictDescription = "";
530
- if (conflicts.length > 0) {
531
- for (const conflict of conflicts) {
205
+ const uniqueConflicts = [...new Set(conflicts)];
206
+ if (uniqueConflicts.length > 0) {
207
+ for (const conflict of uniqueConflicts) {
532
208
  console.warn(`[openapi-to-zod] Warning: allOf composition conflict - ${conflict}`);
533
209
  }
534
- conflictDescription = `allOf property conflicts detected: ${conflicts.join("; ")}`;
535
210
  }
536
211
  const allObjects = schemas.every((s) => s.type === "object" || s.properties || s.$ref || s.allOf);
537
212
  let result;
@@ -559,10 +234,7 @@ function generateAllOf(schemas, isNullable2, context, currentSchema) {
559
234
  }
560
235
  result = merged;
561
236
  }
562
- if (conflictDescription) {
563
- result = `${result}.describe("${conflictDescription}")`;
564
- }
565
- return wrapNullable(result, isNullable2);
237
+ return { schema: wrapNullable(result, isNullable2), conflicts: uniqueConflicts };
566
238
  }
567
239
 
568
240
  // src/validators/number-validator.ts
@@ -586,6 +258,53 @@ function generateNumberValidation(schema, isInt, useDescribe) {
586
258
  return addDescription(validation, schema.description, useDescribe);
587
259
  }
588
260
 
261
+ // src/generators/jsdoc-generator.ts
262
+ import { escapeJSDoc } from "@cerios/openapi-core";
263
+ function generateJSDoc(schema, name, options = { includeDescriptions: true }) {
264
+ if (!schema || typeof schema !== "object") {
265
+ return "";
266
+ }
267
+ if (!options.includeDescriptions) {
268
+ if (schema.deprecated) {
269
+ return "/** @deprecated */\n";
270
+ }
271
+ return "";
272
+ }
273
+ if (!schema.description && !schema.title && !schema.deprecated && !schema.examples && schema.example === void 0) {
274
+ return "";
275
+ }
276
+ const parts = [];
277
+ if (schema.title && typeof schema.title === "string" && (!name || schema.title !== name)) {
278
+ parts.push(escapeJSDoc(schema.title));
279
+ }
280
+ if (schema.description && typeof schema.description === "string") {
281
+ parts.push(escapeJSDoc(schema.description));
282
+ }
283
+ if (schema.examples && Array.isArray(schema.examples) && schema.examples.length > 0) {
284
+ try {
285
+ const examplesStr = schema.examples.map((ex) => JSON.stringify(ex)).join(", ");
286
+ parts.push(`@example ${examplesStr}`);
287
+ } catch (error) {
288
+ console.warn("Warning: Could not serialize schema examples", error);
289
+ }
290
+ } else if (schema.example !== void 0) {
291
+ try {
292
+ parts.push(`@example ${JSON.stringify(schema.example)}`);
293
+ } catch (error) {
294
+ console.warn("Warning: Could not serialize schema example", error);
295
+ }
296
+ }
297
+ if (schema.deprecated) {
298
+ parts.push("@deprecated");
299
+ }
300
+ if (parts.length === 0) {
301
+ return "";
302
+ }
303
+ const fullComment = parts.join(" ");
304
+ return `/** ${fullComment} */
305
+ `;
306
+ }
307
+
589
308
  // src/validators/conditional-validator.ts
590
309
  function generatePropertyAccess(propName) {
591
310
  const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
@@ -644,7 +363,8 @@ function generateConditionalCheck(schema) {
644
363
  for (const [prop, propSchema] of Object.entries(schema.properties)) {
645
364
  const propAccess = generatePropertyAccess(prop);
646
365
  if (propSchema.type) {
647
- conditions.push(`typeof ${propAccess} === "${propSchema.type}"`);
366
+ const schemaType = Array.isArray(propSchema.type) ? propSchema.type[0] : propSchema.type;
367
+ conditions.push(`typeof ${propAccess} === "${schemaType}"`);
648
368
  }
649
369
  if (propSchema.const !== void 0) {
650
370
  const value = typeof propSchema.const === "string" ? `"${propSchema.const}"` : propSchema.const;
@@ -703,7 +423,7 @@ function generateIfThenElse(schema) {
703
423
  if (!thenValid) {
704
424
  ${thenRequiredProps.length > 0 ? `
705
425
  const missingThenProps = ${JSON.stringify(thenRequiredProps)}.filter(p => obj[p] === undefined);
706
- const message = missingThenProps.length > 0
426
+ const message = missingThenProps.length > 0
707
427
  ? \`When condition is met, required properties are missing: \${missingThenProps.join(', ')}\`
708
428
  : "When condition is met, validation constraints failed";
709
429
  ` : `
@@ -721,7 +441,7 @@ function generateIfThenElse(schema) {
721
441
  if (!elseValid) {
722
442
  ${elseRequiredProps2.length > 0 ? `
723
443
  const missingElseProps = ${JSON.stringify(elseRequiredProps2)}.filter(p => obj[p] === undefined);
724
- const message = missingElseProps.length > 0
444
+ const message = missingElseProps.length > 0
725
445
  ? \`When condition is not met, required properties are missing: \${missingElseProps.join(', ')}\`
726
446
  : "When condition is not met, validation constraints failed";
727
447
  ` : `
@@ -746,7 +466,7 @@ function generateIfThenElse(schema) {
746
466
  if (!thenValid) {
747
467
  ${thenRequiredProps.length > 0 ? `
748
468
  const missingProps = ${JSON.stringify(thenRequiredProps)}.filter(p => obj[p] === undefined);
749
- const message = missingProps.length > 0
469
+ const message = missingProps.length > 0
750
470
  ? \`When condition is met, required properties are missing: \${missingProps.join(', ')}\`
751
471
  : "When condition is met, validation constraints failed";
752
472
  ` : `
@@ -771,7 +491,7 @@ function generateIfThenElse(schema) {
771
491
  if (!elseValid) {
772
492
  ${elseRequiredProps.length > 0 ? `
773
493
  const missingProps = ${JSON.stringify(elseRequiredProps)}.filter(p => obj[p] === undefined);
774
- const message = missingProps.length > 0
494
+ const message = missingProps.length > 0
775
495
  ? \`When condition is not met, required properties are missing: \${missingProps.join(', ')}\`
776
496
  : "When condition is not met, validation constraints failed";
777
497
  ` : `
@@ -859,8 +579,9 @@ ${propertyDef}`);
859
579
  case "loose":
860
580
  objectMethod = "z.looseObject";
861
581
  break;
862
- default:
582
+ case "normal":
863
583
  objectMethod = "z.object";
584
+ break;
864
585
  }
865
586
  }
866
587
  let objectDef = `${objectMethod}({
@@ -870,7 +591,7 @@ ${properties.join(",\n")}
870
591
  if (typeof schema.additionalProperties === "object") {
871
592
  const additionalSchema = context.generatePropertySchema(schema.additionalProperties, currentSchema);
872
593
  objectDef += `.catchall(${additionalSchema})`;
873
- } else if (schema.additionalProperties === true) {
594
+ } else if (schema.additionalProperties) {
874
595
  objectDef += ".catchall(z.unknown())";
875
596
  }
876
597
  } else if (schema.patternProperties) {
@@ -995,6 +716,7 @@ ${properties.join(",\n")}
995
716
  }
996
717
 
997
718
  // src/validators/string-validator.ts
719
+ import { escapePattern } from "@cerios/openapi-core";
998
720
  var DEFAULT_FORMAT_MAP = {
999
721
  uuid: "z.uuid()",
1000
722
  email: "z.email()",
@@ -1120,8 +842,32 @@ var _PropertyGenerator = class _PropertyGenerator {
1120
842
  this.filteredPropsCache = /* @__PURE__ */ new Map();
1121
843
  // Performance optimization: LRU cache for generated schemas
1122
844
  this.schemaCache = new LRUCache(500);
845
+ // Track allOf conflicts detected during schema generation
846
+ this.allOfConflicts = [];
847
+ // Schemas that are part of circular dependency chains (need z.lazy for forward refs)
848
+ this.circularDependencies = /* @__PURE__ */ new Set();
1123
849
  this.context = context;
1124
850
  }
851
+ /**
852
+ * Set the schemas that are involved in circular dependency chains.
853
+ * These schemas will use z.lazy() for forward references.
854
+ */
855
+ setCircularDependencies(deps) {
856
+ this.circularDependencies = deps;
857
+ }
858
+ /**
859
+ * Get allOf conflicts detected during the last schema generation
860
+ * @returns Array of conflict description strings
861
+ */
862
+ getAllOfConflicts() {
863
+ return [...this.allOfConflicts];
864
+ }
865
+ /**
866
+ * Clear tracked allOf conflicts (call before generating a new schema)
867
+ */
868
+ clearAllOfConflicts() {
869
+ this.allOfConflicts = [];
870
+ }
1125
871
  /**
1126
872
  * Check if a property should be included based on schemaType and readOnly/writeOnly flags
1127
873
  */
@@ -1154,7 +900,9 @@ var _PropertyGenerator = class _PropertyGenerator {
1154
900
  filterNestedProperties(schema) {
1155
901
  var _a, _b;
1156
902
  const propKeys = schema.properties ? Object.keys(schema.properties).sort().join(",") : "";
1157
- const cacheKey = `${this.context.schemaType}:${schema.type || "unknown"}:${propKeys}:${((_a = schema.required) == null ? void 0 : _a.join(",")) || ""}`;
903
+ const requiredKeys = Array.isArray(schema.required) ? schema.required.join(",") : String((_a = schema.required) != null ? _a : "");
904
+ const schemaType = Array.isArray(schema.type) ? schema.type.join("|") : schema.type || "unknown";
905
+ const cacheKey = `${this.context.schemaType}:${schemaType}:${propKeys}:${requiredKeys}`;
1158
906
  const cached = this.filteredPropsCache.get(cacheKey);
1159
907
  if (cached) {
1160
908
  return cached;
@@ -1250,7 +998,7 @@ var _PropertyGenerator = class _PropertyGenerator {
1250
998
  const schema = (_b = (_a = this.context.spec.components) == null ? void 0 : _a.schemas) == null ? void 0 : _b[schemaName];
1251
999
  if (!schema) return schemaName;
1252
1000
  if (schema.allOf && schema.allOf.length === 1 && schema.allOf[0].$ref && !schema.properties && !schema.oneOf && !schema.anyOf) {
1253
- const targetName = resolveRef(schema.allOf[0].$ref);
1001
+ const targetName = resolveRefName(schema.allOf[0].$ref);
1254
1002
  return this.resolveSchemaAlias(targetName);
1255
1003
  }
1256
1004
  return schemaName;
@@ -1263,7 +1011,7 @@ var _PropertyGenerator = class _PropertyGenerator {
1263
1011
  const toSchemaSpec = (_b = (_a = this.context.spec.components) == null ? void 0 : _a.schemas) == null ? void 0 : _b[toSchema];
1264
1012
  if (!toSchemaSpec) return false;
1265
1013
  if (toSchemaSpec.allOf && toSchemaSpec.allOf.length === 1 && toSchemaSpec.allOf[0].$ref) {
1266
- const aliasTarget = resolveRef(toSchemaSpec.allOf[0].$ref);
1014
+ const aliasTarget = resolveRefName(toSchemaSpec.allOf[0].$ref);
1267
1015
  return aliasTarget === fromSchema;
1268
1016
  }
1269
1017
  return false;
@@ -1364,7 +1112,7 @@ var _PropertyGenerator = class _PropertyGenerator {
1364
1112
  return wrapNullable(union, nullable);
1365
1113
  }
1366
1114
  if (schema.$ref) {
1367
- const refName = resolveRef(schema.$ref);
1115
+ const refName = resolveRefName(schema.$ref);
1368
1116
  const resolvedRefName = this.resolveSchemaAlias(refName);
1369
1117
  if (currentSchema && refName !== currentSchema && !isTopLevel) {
1370
1118
  if (!this.context.schemaDependencies.has(currentSchema)) {
@@ -1374,8 +1122,13 @@ var _PropertyGenerator = class _PropertyGenerator {
1374
1122
  }
1375
1123
  const strippedRefName = stripPrefix(resolvedRefName, this.context.stripSchemaPrefix);
1376
1124
  const schemaName = `${toCamelCase(strippedRefName, this.context.namingOptions)}Schema`;
1377
- if (currentSchema && (refName === currentSchema || this.isCircularThroughAlias(currentSchema, refName))) {
1378
- const lazySchema = `z.lazy((): z.ZodTypeAny => ${schemaName})`;
1125
+ const typeName = toPascalCase(strippedRefName);
1126
+ const isDirectSelfRef = currentSchema && refName === currentSchema;
1127
+ const isCircularAlias = currentSchema && this.isCircularThroughAlias(currentSchema, refName);
1128
+ const isMutuallyCircular = currentSchema && this.circularDependencies.has(currentSchema) && this.circularDependencies.has(refName);
1129
+ if (isDirectSelfRef || isCircularAlias || isMutuallyCircular) {
1130
+ const lazyTypeAnnotation = this.context.separateTypesFile ? `z.ZodType<${typeName}>` : "z.ZodTypeAny";
1131
+ const lazySchema = `z.lazy((): ${lazyTypeAnnotation} => ${schemaName})`;
1379
1132
  return wrapNullable(lazySchema, nullable);
1380
1133
  }
1381
1134
  return wrapNullable(schemaName, nullable);
@@ -1408,7 +1161,7 @@ var _PropertyGenerator = class _PropertyGenerator {
1408
1161
  }
1409
1162
  if (schema.allOf) {
1410
1163
  const compositionNullable = isNullable(schema, false);
1411
- let composition = generateAllOf(
1164
+ const allOfResult = generateAllOf(
1412
1165
  schema.allOf,
1413
1166
  compositionNullable,
1414
1167
  {
@@ -1418,6 +1171,10 @@ var _PropertyGenerator = class _PropertyGenerator {
1418
1171
  },
1419
1172
  currentSchema
1420
1173
  );
1174
+ if (allOfResult.conflicts.length > 0) {
1175
+ this.allOfConflicts.push(...allOfResult.conflicts);
1176
+ }
1177
+ let composition = allOfResult.schema;
1421
1178
  if (schema.unevaluatedProperties !== void 0) {
1422
1179
  composition = this.applyUnevaluatedProperties(composition, schema);
1423
1180
  }
@@ -1529,13 +1286,14 @@ var _PropertyGenerator = class _PropertyGenerator {
1529
1286
  case "loose":
1530
1287
  validation = "z.looseObject({})";
1531
1288
  break;
1532
- default:
1289
+ case "record":
1533
1290
  validation = "z.record(z.string(), z.unknown())";
1534
1291
  break;
1535
1292
  }
1536
1293
  validation = addDescription(validation, schema.description, this.context.useDescribe);
1537
1294
  }
1538
1295
  break;
1296
+ case void 0:
1539
1297
  default:
1540
1298
  validation = "z.unknown()";
1541
1299
  validation = addDescription(validation, schema.description, this.context.useDescribe);
@@ -1594,194 +1352,71 @@ _PropertyGenerator.INCLUSION_RULES = {
1594
1352
  };
1595
1353
  var PropertyGenerator = _PropertyGenerator;
1596
1354
 
1597
- // src/utils/operation-filters.ts
1598
- import { minimatch as minimatch2 } from "minimatch";
1599
- function createFilterStatistics() {
1600
- return {
1601
- totalOperations: 0,
1602
- includedOperations: 0,
1603
- filteredByTags: 0,
1604
- filteredByPaths: 0,
1605
- filteredByMethods: 0,
1606
- filteredByOperationIds: 0,
1607
- filteredByDeprecated: 0
1608
- };
1609
- }
1610
- function matchesAnyPattern(value, patterns) {
1611
- if (!patterns || patterns.length === 0) {
1612
- return false;
1613
- }
1614
- if (!value) {
1615
- return false;
1616
- }
1617
- return patterns.some((pattern) => minimatch2(value, pattern));
1618
- }
1619
- function containsAny(arr, values) {
1620
- if (!values || values.length === 0) {
1621
- return false;
1355
+ // src/openapi-generator.ts
1356
+ import { existsSync, mkdirSync, writeFileSync } from "fs";
1357
+ import { dirname, normalize, relative } from "path";
1358
+ import {
1359
+ analyzeSchemaUsage,
1360
+ ConfigurationError,
1361
+ createFilterStatistics,
1362
+ detectCircularReferences,
1363
+ expandTransitiveReferences,
1364
+ extractSchemaRefs,
1365
+ formatFilterStatistics,
1366
+ getOperationName,
1367
+ LRUCache as LRUCache2,
1368
+ loadOpenAPISpec,
1369
+ mergeParameters,
1370
+ resolveRefName as resolveRefName2,
1371
+ SchemaGenerationError,
1372
+ SpecValidationError,
1373
+ shouldIncludeOperation,
1374
+ stripPathPrefix,
1375
+ stripPrefix as stripPrefix2,
1376
+ toCamelCase as toCamelCase3,
1377
+ toPascalCase as toPascalCase3,
1378
+ validateFilters
1379
+ } from "@cerios/openapi-core";
1380
+ import { TypeScriptGenerator } from "@cerios/openapi-to-typescript";
1381
+ import { minimatch } from "minimatch";
1382
+
1383
+ // src/generators/enum-generator.ts
1384
+ import { toCamelCase as toCamelCase2, toPascalCase as toPascalCase2 } from "@cerios/openapi-core";
1385
+ function generateEnum(name, values, options) {
1386
+ const schemaName = `${toCamelCase2(name, options)}Schema`;
1387
+ const typeName = toPascalCase2(name);
1388
+ const allBooleans = values.every((v) => typeof v === "boolean");
1389
+ if (allBooleans) {
1390
+ const schemaCode2 = `export const ${schemaName} = z.boolean();`;
1391
+ const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
1392
+ return { schemaCode: schemaCode2, typeCode: typeCode2 };
1622
1393
  }
1623
- if (!arr || arr.length === 0) {
1624
- return false;
1394
+ const allStrings = values.every((v) => typeof v === "string");
1395
+ if (allStrings) {
1396
+ const enumValues = values.map((v) => `"${v}"`).join(", ");
1397
+ const schemaCode2 = `export const ${schemaName} = z.enum([${enumValues}]);`;
1398
+ const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
1399
+ return { schemaCode: schemaCode2, typeCode: typeCode2 };
1625
1400
  }
1626
- return values.some((value) => arr.includes(value));
1627
- }
1628
- function shouldIncludeOperation(operation, path, method, filters, stats) {
1629
- if (!filters) {
1630
- return true;
1631
- }
1632
- const methodLower = method.toLowerCase();
1633
- const operationId = operation == null ? void 0 : operation.operationId;
1634
- const tags = (operation == null ? void 0 : operation.tags) || [];
1635
- const deprecated = (operation == null ? void 0 : operation.deprecated) === true;
1636
- if (filters.includeTags && filters.includeTags.length > 0) {
1637
- if (!containsAny(tags, filters.includeTags)) {
1638
- if (stats) stats.filteredByTags++;
1639
- return false;
1401
+ const literalValues = values.map((v) => {
1402
+ if (typeof v === "string") {
1403
+ return `z.literal("${v}")`;
1640
1404
  }
1641
- }
1642
- if (filters.includePaths && filters.includePaths.length > 0) {
1643
- if (!matchesAnyPattern(path, filters.includePaths)) {
1644
- if (stats) stats.filteredByPaths++;
1645
- return false;
1646
- }
1647
- }
1648
- if (filters.includeMethods && filters.includeMethods.length > 0) {
1649
- const methodsLower = filters.includeMethods.map((m) => m.toLowerCase());
1650
- if (!methodsLower.includes(methodLower)) {
1651
- if (stats) stats.filteredByMethods++;
1652
- return false;
1653
- }
1654
- }
1655
- if (filters.includeOperationIds && filters.includeOperationIds.length > 0) {
1656
- if (!matchesAnyPattern(operationId, filters.includeOperationIds)) {
1657
- if (stats) stats.filteredByOperationIds++;
1658
- return false;
1659
- }
1660
- }
1661
- if (filters.excludeDeprecated === true && deprecated) {
1662
- if (stats) stats.filteredByDeprecated++;
1663
- return false;
1664
- }
1665
- if (filters.excludeTags && filters.excludeTags.length > 0) {
1666
- if (containsAny(tags, filters.excludeTags)) {
1667
- if (stats) stats.filteredByTags++;
1668
- return false;
1669
- }
1670
- }
1671
- if (filters.excludePaths && filters.excludePaths.length > 0) {
1672
- if (matchesAnyPattern(path, filters.excludePaths)) {
1673
- if (stats) stats.filteredByPaths++;
1674
- return false;
1675
- }
1676
- }
1677
- if (filters.excludeMethods && filters.excludeMethods.length > 0) {
1678
- const methodsLower = filters.excludeMethods.map((m) => m.toLowerCase());
1679
- if (methodsLower.includes(methodLower)) {
1680
- if (stats) stats.filteredByMethods++;
1681
- return false;
1682
- }
1683
- }
1684
- if (filters.excludeOperationIds && filters.excludeOperationIds.length > 0) {
1685
- if (matchesAnyPattern(operationId, filters.excludeOperationIds)) {
1686
- if (stats) stats.filteredByOperationIds++;
1687
- return false;
1688
- }
1689
- }
1690
- return true;
1691
- }
1692
- function validateFilters(stats, filters) {
1693
- if (!filters || stats.totalOperations === 0) {
1694
- return;
1695
- }
1696
- if (stats.includedOperations === 0) {
1697
- console.warn(
1698
- `\u26A0\uFE0F Warning: All ${stats.totalOperations} operations were filtered out. Check your operationFilters configuration.`
1699
- );
1700
- const filterBreakdown = [];
1701
- if (stats.filteredByTags > 0) filterBreakdown.push(`${stats.filteredByTags} by tags`);
1702
- if (stats.filteredByPaths > 0) filterBreakdown.push(`${stats.filteredByPaths} by paths`);
1703
- if (stats.filteredByMethods > 0) filterBreakdown.push(`${stats.filteredByMethods} by methods`);
1704
- if (stats.filteredByOperationIds > 0) filterBreakdown.push(`${stats.filteredByOperationIds} by operationIds`);
1705
- if (stats.filteredByDeprecated > 0) filterBreakdown.push(`${stats.filteredByDeprecated} by deprecated flag`);
1706
- if (filterBreakdown.length > 0) {
1707
- console.warn(` Filtered: ${filterBreakdown.join(", ")}`);
1708
- }
1709
- }
1710
- }
1711
- function formatFilterStatistics(stats) {
1712
- if (stats.totalOperations === 0) {
1713
- return "";
1714
- }
1715
- const lines = [];
1716
- lines.push("Operation Filtering:");
1717
- lines.push(` Total operations: ${stats.totalOperations}`);
1718
- lines.push(` Included operations: ${stats.includedOperations}`);
1719
- const filteredCount = stats.filteredByTags + stats.filteredByPaths + stats.filteredByMethods + stats.filteredByOperationIds + stats.filteredByDeprecated;
1720
- if (filteredCount > 0) {
1721
- lines.push(` Filtered operations: ${filteredCount}`);
1722
- if (stats.filteredByTags > 0) lines.push(` - By tags: ${stats.filteredByTags}`);
1723
- if (stats.filteredByPaths > 0) lines.push(` - By paths: ${stats.filteredByPaths}`);
1724
- if (stats.filteredByMethods > 0) lines.push(` - By methods: ${stats.filteredByMethods}`);
1725
- if (stats.filteredByOperationIds > 0) lines.push(` - By operationIds: ${stats.filteredByOperationIds}`);
1726
- if (stats.filteredByDeprecated > 0) lines.push(` - By deprecated: ${stats.filteredByDeprecated}`);
1727
- }
1728
- return lines.join("\n");
1405
+ return `z.literal(${v})`;
1406
+ }).join(", ");
1407
+ const schemaCode = `export const ${schemaName} = z.union([${literalValues}]);`;
1408
+ const typeCode = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
1409
+ return { schemaCode, typeCode };
1729
1410
  }
1730
1411
 
1731
- // src/utils/ref-resolver.ts
1732
- function resolveRef2(obj, spec, maxDepth = 10) {
1733
- var _a, _b, _c, _d;
1734
- if (!obj || typeof obj !== "object" || maxDepth <= 0) return obj;
1735
- if (!obj.$ref) return obj;
1736
- const ref = obj.$ref;
1737
- let resolved = null;
1738
- const paramMatch = ref.match(/^#\/components\/parameters\/(.+)$/);
1739
- const requestBodyMatch = ref.match(/^#\/components\/requestBodies\/(.+)$/);
1740
- const responseMatch = ref.match(/^#\/components\/responses\/(.+)$/);
1741
- const schemaMatch = ref.match(/^#\/components\/schemas\/(.+)$/);
1742
- if (paramMatch && ((_a = spec.components) == null ? void 0 : _a.parameters)) {
1743
- const name = paramMatch[1];
1744
- resolved = spec.components.parameters[name];
1745
- } else if (requestBodyMatch && ((_b = spec.components) == null ? void 0 : _b.requestBodies)) {
1746
- const name = requestBodyMatch[1];
1747
- resolved = spec.components.requestBodies[name];
1748
- } else if (responseMatch && ((_c = spec.components) == null ? void 0 : _c.responses)) {
1749
- const name = responseMatch[1];
1750
- resolved = spec.components.responses[name];
1751
- } else if (schemaMatch && ((_d = spec.components) == null ? void 0 : _d.schemas)) {
1752
- const name = schemaMatch[1];
1753
- resolved = spec.components.schemas[name];
1754
- }
1755
- if (resolved) {
1756
- if (resolved.$ref) {
1757
- return resolveRef2(resolved, spec, maxDepth - 1);
1758
- }
1759
- return resolved;
1760
- }
1761
- return obj;
1762
- }
1763
- function resolveParameterRef(param, spec) {
1764
- return resolveRef2(param, spec);
1412
+ // src/openapi-generator.ts
1413
+ var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options"];
1414
+ function isResolvedParameter(param) {
1415
+ return typeof param === "object" && param !== null && "name" in param && typeof param.name === "string" && "in" in param;
1765
1416
  }
1766
- function mergeParameters(pathParams, operationParams, spec) {
1767
- const resolvedPathParams = (pathParams || []).map((p) => resolveParameterRef(p, spec));
1768
- const resolvedOperationParams = (operationParams || []).map((p) => resolveParameterRef(p, spec));
1769
- const merged = [...resolvedPathParams];
1770
- for (const opParam of resolvedOperationParams) {
1771
- if (!opParam || typeof opParam !== "object") continue;
1772
- const existingIndex = merged.findIndex(
1773
- (p) => p && typeof p === "object" && p.name === opParam.name && p.in === opParam.in
1774
- );
1775
- if (existingIndex >= 0) {
1776
- merged[existingIndex] = opParam;
1777
- } else {
1778
- merged.push(opParam);
1779
- }
1780
- }
1781
- return merged;
1417
+ function isOpenAPIPathItem(value) {
1418
+ return typeof value === "object" && value !== null;
1782
1419
  }
1783
-
1784
- // src/openapi-generator.ts
1785
1420
  var OpenApiGenerator = class {
1786
1421
  constructor(options) {
1787
1422
  this.schemas = /* @__PURE__ */ new Map();
@@ -1790,89 +1425,48 @@ var OpenApiGenerator = class {
1790
1425
  this.schemaUsageMap = /* @__PURE__ */ new Map();
1791
1426
  this.needsZodImport = true;
1792
1427
  this.filterStats = createFilterStatistics();
1793
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
1428
+ /** Track total allOf conflicts detected across all schemas */
1429
+ this.allOfConflictCount = 0;
1430
+ /** Track schemas involved in circular dependency chains */
1431
+ this.circularDependencies = /* @__PURE__ */ new Set();
1432
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
1794
1433
  if (!options.input) {
1795
1434
  throw new ConfigurationError("Input path is required", { providedOptions: options });
1796
1435
  }
1436
+ this.separateSchemasMode = Boolean(options.outputZodSchemas);
1797
1437
  this.options = {
1798
1438
  mode: options.mode || "normal",
1799
1439
  input: options.input,
1800
- output: options.output,
1801
- includeDescriptions: (_a = options.includeDescriptions) != null ? _a : true,
1802
- useDescribe: (_b = options.useDescribe) != null ? _b : false,
1803
- defaultNullable: (_c = options.defaultNullable) != null ? _c : false,
1804
- emptyObjectBehavior: (_d = options.emptyObjectBehavior) != null ? _d : "loose",
1440
+ outputTypes: options.outputTypes,
1441
+ outputZodSchemas: options.outputZodSchemas,
1442
+ enumFormat: options.enumFormat,
1443
+ typeAssertionThreshold: (_a = options.typeAssertionThreshold) != null ? _a : 0,
1444
+ includeDescriptions: (_b = options.includeDescriptions) != null ? _b : true,
1445
+ useDescribe: (_c = options.useDescribe) != null ? _c : false,
1446
+ defaultNullable: (_d = options.defaultNullable) != null ? _d : false,
1447
+ emptyObjectBehavior: (_e = options.emptyObjectBehavior) != null ? _e : "loose",
1805
1448
  schemaType: options.schemaType || "all",
1806
1449
  prefix: options.prefix,
1807
1450
  suffix: options.suffix,
1808
1451
  stripSchemaPrefix: options.stripSchemaPrefix,
1809
1452
  stripPathPrefix: options.stripPathPrefix,
1810
- showStats: (_e = options.showStats) != null ? _e : true,
1453
+ useOperationId: (_f = options.useOperationId) != null ? _f : true,
1454
+ showStats: (_g = options.showStats) != null ? _g : true,
1811
1455
  request: options.request,
1812
1456
  response: options.response,
1813
1457
  operationFilters: options.operationFilters,
1814
1458
  ignoreHeaders: options.ignoreHeaders,
1815
- cacheSize: (_f = options.cacheSize) != null ? _f : 1e3,
1816
- batchSize: (_g = options.batchSize) != null ? _g : 10,
1459
+ cacheSize: (_h = options.cacheSize) != null ? _h : 1e3,
1460
+ batchSize: (_i = options.batchSize) != null ? _i : 10,
1817
1461
  customDateTimeFormatRegex: options.customDateTimeFormatRegex
1818
1462
  };
1819
- this.patternCache = new LRUCache((_h = this.options.cacheSize) != null ? _h : 1e3);
1463
+ this.patternCache = new LRUCache2((_j = this.options.cacheSize) != null ? _j : 1e3);
1820
1464
  this.dateTimeValidation = buildDateTimeValidation(this.options.customDateTimeFormatRegex);
1821
- try {
1822
- const fs = __require("fs");
1823
- if (!fs.existsSync(this.options.input)) {
1824
- throw new FileOperationError(`Input file not found: ${this.options.input}`, this.options.input);
1825
- }
1826
- } catch (error) {
1827
- if (error instanceof FileOperationError) {
1828
- throw error;
1829
- }
1830
- }
1831
- try {
1832
- const content = readFileSync(this.options.input, "utf-8");
1833
- try {
1834
- this.spec = parse(content);
1835
- } catch (yamlError) {
1836
- try {
1837
- this.spec = JSON.parse(content);
1838
- } catch {
1839
- if (yamlError instanceof Error) {
1840
- const errorMessage = [
1841
- `Failed to parse OpenAPI specification from: ${this.options.input}`,
1842
- "",
1843
- `Error: ${yamlError.message}`,
1844
- "",
1845
- "Please ensure:",
1846
- " - The file exists and is readable",
1847
- " - The file contains valid YAML or JSON syntax",
1848
- " - The file is a valid OpenAPI 3.x specification"
1849
- ].join("\n");
1850
- throw new SpecValidationError(errorMessage, {
1851
- filePath: this.options.input,
1852
- originalError: yamlError.message
1853
- });
1854
- }
1855
- throw yamlError;
1856
- }
1857
- }
1858
- } catch (error) {
1859
- if (error instanceof SpecValidationError) {
1860
- throw error;
1861
- }
1862
- if (error instanceof Error) {
1863
- const errorMessage = [
1864
- `Failed to read OpenAPI specification from: ${this.options.input}`,
1865
- "",
1866
- `Error: ${error.message}`
1867
- ].join("\n");
1868
- throw new SpecValidationError(errorMessage, { filePath: this.options.input, originalError: error.message });
1869
- }
1870
- throw error;
1871
- }
1465
+ this.spec = loadOpenAPISpec(this.options.input);
1872
1466
  this.validateSpec();
1873
1467
  this.requestOptions = this.resolveOptionsForContext("request");
1874
1468
  this.responseOptions = this.resolveOptionsForContext("response");
1875
- this.analyzeSchemaUsage();
1469
+ this.initializeSchemaUsage();
1876
1470
  this.propertyGenerator = new PropertyGenerator({
1877
1471
  spec: this.spec,
1878
1472
  schemaDependencies: this.schemaDependencies,
@@ -1880,19 +1474,21 @@ var OpenApiGenerator = class {
1880
1474
  mode: this.requestOptions.mode,
1881
1475
  includeDescriptions: this.requestOptions.includeDescriptions,
1882
1476
  useDescribe: this.requestOptions.useDescribe,
1883
- defaultNullable: (_i = this.options.defaultNullable) != null ? _i : false,
1884
- emptyObjectBehavior: (_j = this.options.emptyObjectBehavior) != null ? _j : "loose",
1477
+ defaultNullable: (_k = this.options.defaultNullable) != null ? _k : false,
1478
+ emptyObjectBehavior: (_l = this.options.emptyObjectBehavior) != null ? _l : "loose",
1885
1479
  namingOptions: {
1886
1480
  prefix: this.options.prefix,
1887
1481
  suffix: this.options.suffix
1888
1482
  },
1889
1483
  stripSchemaPrefix: this.options.stripSchemaPrefix,
1890
1484
  dateTimeValidation: this.dateTimeValidation,
1891
- patternCache: this.patternCache
1485
+ patternCache: this.patternCache,
1486
+ separateTypesFile: this.separateSchemasMode
1892
1487
  });
1893
1488
  }
1894
1489
  /**
1895
1490
  * Generate schemas as a string (without writing to file)
1491
+ * When separateSchemasMode is active, generates Zod schemas with explicit type annotations
1896
1492
  * @returns The generated TypeScript code as a string
1897
1493
  */
1898
1494
  generateString() {
@@ -1900,6 +1496,11 @@ var OpenApiGenerator = class {
1900
1496
  if (!((_a = this.spec.components) == null ? void 0 : _a.schemas)) {
1901
1497
  throw new SpecValidationError("No schemas found in OpenAPI spec", { filePath: this.options.input });
1902
1498
  }
1499
+ if (this.separateSchemasMode) {
1500
+ return this.generateSeparateSchemasString();
1501
+ }
1502
+ this.analyzeCircularDependencies();
1503
+ this.propertyGenerator.setCircularDependencies(this.circularDependencies);
1903
1504
  for (const [name, schema] of Object.entries(this.spec.components.schemas)) {
1904
1505
  if (this.options.operationFilters && this.schemaUsageMap.size > 0 && !this.schemaUsageMap.has(name)) {
1905
1506
  continue;
@@ -1925,10 +1526,10 @@ var OpenApiGenerator = class {
1925
1526
  const typeCode = this.types.get(name);
1926
1527
  if (schemaCode) {
1927
1528
  output.push(schemaCode);
1928
- const strippedName = stripPrefix(name, this.options.stripSchemaPrefix);
1929
- const typeName = toPascalCase(strippedName);
1529
+ const strippedName = stripPrefix2(name, this.options.stripSchemaPrefix);
1530
+ const typeName = toPascalCase3(strippedName);
1930
1531
  if (!schemaCode.includes(`export type ${typeName}`)) {
1931
- const schemaName = `${toCamelCase(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
1532
+ const schemaName = `${toCamelCase3(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
1932
1533
  output.push(`export type ${typeName} = z.infer<typeof ${schemaName}>;`);
1933
1534
  }
1934
1535
  output.push("");
@@ -1950,14 +1551,224 @@ var OpenApiGenerator = class {
1950
1551
  }
1951
1552
  }
1952
1553
  /**
1953
- * Generate the complete output file
1554
+ * Generate the complete output file(s)
1555
+ * When separateSchemasMode is active, generates both types and schemas files
1954
1556
  */
1955
1557
  generate() {
1956
- const output = this.generateString();
1957
- const normalizedOutput = normalize(this.options.output);
1958
- this.ensureDirectoryExists(normalizedOutput);
1959
- writeFileSync(normalizedOutput, output);
1960
- console.log(` \u2713 Generated ${normalizedOutput}`);
1558
+ if (this.separateSchemasMode) {
1559
+ const typesContent = this.generateTypesString();
1560
+ const schemasContent = this.generateString();
1561
+ const normalizedTypes = normalize(this.options.outputTypes);
1562
+ this.ensureDirectoryExists(normalizedTypes);
1563
+ writeFileSync(normalizedTypes, typesContent, "utf-8");
1564
+ console.log(` \u2713 Generated ${normalizedTypes}`);
1565
+ if (!this.options.outputZodSchemas) {
1566
+ throw new Error("Internal error: outputZodSchemas should be defined in separateSchemasMode");
1567
+ }
1568
+ const outputZodSchemas = this.options.outputZodSchemas;
1569
+ const normalizedSchemas = normalize(outputZodSchemas);
1570
+ this.ensureDirectoryExists(normalizedSchemas);
1571
+ writeFileSync(normalizedSchemas, schemasContent, "utf-8");
1572
+ console.log(` \u2713 Generated ${normalizedSchemas}`);
1573
+ } else {
1574
+ const output = this.generateString();
1575
+ const normalizedOutput = normalize(this.options.outputTypes);
1576
+ this.ensureDirectoryExists(normalizedOutput);
1577
+ writeFileSync(normalizedOutput, output);
1578
+ console.log(` \u2713 Generated ${normalizedOutput}`);
1579
+ }
1580
+ }
1581
+ /**
1582
+ * Generate Zod schemas with explicit type annotations (for outputZodSchemas mode)
1583
+ * Generates schemas like: `export const userSchema: z.ZodType<User> = z.object({...})`
1584
+ * @returns The generated Zod schemas TypeScript code
1585
+ */
1586
+ generateSeparateSchemasString() {
1587
+ var _a;
1588
+ const schemas = (_a = this.spec.components) == null ? void 0 : _a.schemas;
1589
+ if (!schemas) {
1590
+ return "";
1591
+ }
1592
+ if (!this.options.outputZodSchemas) {
1593
+ throw new Error("Internal error: outputZodSchemas should be defined in separateSchemasMode");
1594
+ }
1595
+ const outputZodSchemas = this.options.outputZodSchemas;
1596
+ this.analyzeCircularDependencies();
1597
+ this.propertyGenerator.setCircularDependencies(this.circularDependencies);
1598
+ for (const [name, schema] of Object.entries(schemas)) {
1599
+ if (this.options.operationFilters && this.schemaUsageMap.size > 0 && !this.schemaUsageMap.has(name)) {
1600
+ continue;
1601
+ }
1602
+ this.generateComponentSchema(name, schema);
1603
+ }
1604
+ this.generateQueryParameterSchemas();
1605
+ this.generateHeaderParameterSchemas();
1606
+ validateFilters(this.filterStats, this.options.operationFilters);
1607
+ const orderedSchemaNames = this.topologicalSort();
1608
+ const output = ["// Auto-generated by @cerios/openapi-to-zod", "// Do not edit this file manually", ""];
1609
+ if (this.options.showStats === true) {
1610
+ output.push(...this.generateStats());
1611
+ output.push("");
1612
+ }
1613
+ output.push('import { z } from "zod";');
1614
+ const typesImportPath = this.calculateRelativeImportPath(outputZodSchemas, this.options.outputTypes);
1615
+ const typeNames = [];
1616
+ for (const name of orderedSchemaNames) {
1617
+ const strippedName = stripPrefix2(name, this.options.stripSchemaPrefix);
1618
+ const typeName = toPascalCase3(strippedName);
1619
+ typeNames.push(typeName);
1620
+ }
1621
+ if (typeNames.length > 0) {
1622
+ output.push(`import type { ${typeNames.join(", ")} } from "${typesImportPath}";`);
1623
+ }
1624
+ output.push("");
1625
+ output.push("// Schemas");
1626
+ for (const name of orderedSchemaNames) {
1627
+ const schemaCode = this.schemas.get(name);
1628
+ if (schemaCode) {
1629
+ const strippedName = stripPrefix2(name, this.options.stripSchemaPrefix);
1630
+ const typeName = toPascalCase3(strippedName);
1631
+ const schemaName = `${toCamelCase3(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
1632
+ const schemaDefinition = this.isRecordObject(schemas[name]) ? schemas[name] : void 0;
1633
+ const transformedCode = this.addExplicitTypeAnnotation(schemaCode, schemaName, typeName, schemaDefinition);
1634
+ output.push(transformedCode);
1635
+ output.push("");
1636
+ }
1637
+ }
1638
+ return output.join("\n");
1639
+ }
1640
+ /**
1641
+ * Generate TypeScript types as a string (for outputZodSchemas mode)
1642
+ * Uses @cerios/openapi-to-typescript internally
1643
+ * @returns The generated TypeScript types code
1644
+ */
1645
+ generateTypesString() {
1646
+ var _a;
1647
+ const tsGenerator = new TypeScriptGenerator({
1648
+ input: this.options.input,
1649
+ outputTypes: this.options.outputTypes,
1650
+ includeDescriptions: this.options.includeDescriptions,
1651
+ defaultNullable: this.options.defaultNullable,
1652
+ prefix: this.options.prefix,
1653
+ suffix: this.options.suffix,
1654
+ stripSchemaPrefix: this.options.stripSchemaPrefix,
1655
+ stripPathPrefix: this.options.stripPathPrefix,
1656
+ operationFilters: this.options.operationFilters,
1657
+ showStats: this.options.showStats,
1658
+ enumFormat: (_a = this.options.enumFormat) != null ? _a : "const-object"
1659
+ });
1660
+ return tsGenerator.generateString();
1661
+ }
1662
+ /**
1663
+ * Add explicit type annotation to a schema declaration
1664
+ * Transforms: `export const userSchema = z.object({...})`
1665
+ * To: `export const userSchema: z.ZodType<User> = z.object({...})` (annotation)
1666
+ * Or: `export const userSchema = z.object({...}) as unknown as z.ZodType<User>` (double assertion)
1667
+ *
1668
+ * Uses double assertion via `unknown` when typeAssertionThreshold is set and schema complexity
1669
+ * meets or exceeds the threshold. This completely bypasses TypeScript's structural checking
1670
+ * to avoid "Type instantiation is excessively deep" errors on very large schemas.
1671
+ *
1672
+ * Also removes any `export type X = z.infer<...>` lines since types
1673
+ * are imported from the separate types file.
1674
+ */
1675
+ addExplicitTypeAnnotation(schemaCode, schemaName, typeName, schemaDefinition) {
1676
+ var _a;
1677
+ const code = schemaCode.replace(/\nexport type \w+ = z\.infer<typeof \w+>;/g, "");
1678
+ const jsdocMatch = code.match(/^(\/\*\*[\s\S]*?\*\/\n)?/);
1679
+ const jsdoc = (jsdocMatch == null ? void 0 : jsdocMatch[1]) || "";
1680
+ const codeWithoutJsdoc = code.slice(jsdoc.length);
1681
+ const threshold = (_a = this.options.typeAssertionThreshold) != null ? _a : 0;
1682
+ const useAssertion = threshold > 0 && schemaDefinition && this.calculateSchemaComplexity(schemaDefinition) >= threshold;
1683
+ const pattern = new RegExp(`export const ${schemaName} = `);
1684
+ if (pattern.test(codeWithoutJsdoc)) {
1685
+ let schemaBody = codeWithoutJsdoc.replace(pattern, "");
1686
+ if (useAssertion) {
1687
+ schemaBody = schemaBody.replace(/;$/, "");
1688
+ return `${jsdoc}export const ${schemaName} = ${schemaBody} as unknown as z.ZodType<${typeName}>;`;
1689
+ }
1690
+ return `${jsdoc}export const ${schemaName}: z.ZodType<${typeName}> = ${schemaBody}`;
1691
+ }
1692
+ return code;
1693
+ }
1694
+ /**
1695
+ * Type guard to check if a value is a Record<string, unknown>
1696
+ */
1697
+ isRecordObject(value) {
1698
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1699
+ }
1700
+ /**
1701
+ * Calculate the complexity of a schema for threshold comparison
1702
+ * Complexity formula: properties + (nested levels * 10) + (array/union members * 2)
1703
+ */
1704
+ calculateSchemaComplexity(schema, depth = 0) {
1705
+ if (!schema || typeof schema !== "object") {
1706
+ return 0;
1707
+ }
1708
+ let complexity = depth * 10;
1709
+ if (schema.$ref) {
1710
+ return complexity + 5;
1711
+ }
1712
+ const properties = schema.properties;
1713
+ if (this.isRecordObject(properties)) {
1714
+ const propCount = Object.keys(properties).length;
1715
+ complexity += propCount;
1716
+ for (const prop of Object.values(properties)) {
1717
+ if (this.isRecordObject(prop)) {
1718
+ complexity += this.calculateSchemaComplexity(prop, depth + 1);
1719
+ }
1720
+ }
1721
+ }
1722
+ const allOf = schema.allOf;
1723
+ if (Array.isArray(allOf)) {
1724
+ complexity += allOf.length * 2;
1725
+ for (const subSchema of allOf) {
1726
+ if (this.isRecordObject(subSchema)) {
1727
+ complexity += this.calculateSchemaComplexity(subSchema, depth + 1);
1728
+ }
1729
+ }
1730
+ }
1731
+ const oneOf = schema.oneOf;
1732
+ if (Array.isArray(oneOf)) {
1733
+ complexity += oneOf.length * 2;
1734
+ for (const subSchema of oneOf) {
1735
+ if (this.isRecordObject(subSchema)) {
1736
+ complexity += this.calculateSchemaComplexity(subSchema, depth + 1);
1737
+ }
1738
+ }
1739
+ }
1740
+ const anyOf = schema.anyOf;
1741
+ if (Array.isArray(anyOf)) {
1742
+ complexity += anyOf.length * 2;
1743
+ for (const subSchema of anyOf) {
1744
+ if (this.isRecordObject(subSchema)) {
1745
+ complexity += this.calculateSchemaComplexity(subSchema, depth + 1);
1746
+ }
1747
+ }
1748
+ }
1749
+ const items = schema.items;
1750
+ if (this.isRecordObject(items)) {
1751
+ complexity += 2;
1752
+ complexity += this.calculateSchemaComplexity(items, depth + 1);
1753
+ }
1754
+ const additionalProps = schema.additionalProperties;
1755
+ if (this.isRecordObject(additionalProps)) {
1756
+ complexity += 2;
1757
+ complexity += this.calculateSchemaComplexity(additionalProps, depth + 1);
1758
+ }
1759
+ return complexity;
1760
+ }
1761
+ /**
1762
+ * Calculate relative import path from schema file to types file
1763
+ */
1764
+ calculateRelativeImportPath(fromPath, toPath) {
1765
+ const fromDir = dirname(normalize(fromPath));
1766
+ const toFile = normalize(toPath).replace(/\.[tj]s$/, "");
1767
+ let relativePath = relative(fromDir, toFile);
1768
+ if (!relativePath.startsWith(".") && !relativePath.startsWith("..")) {
1769
+ relativePath = `./${relativePath}`;
1770
+ }
1771
+ return relativePath.replace(/\\/g, "/");
1961
1772
  }
1962
1773
  /**
1963
1774
  * Resolve options for a specific context (request or response)
@@ -1973,191 +1784,84 @@ var OpenApiGenerator = class {
1973
1784
  };
1974
1785
  }
1975
1786
  /**
1976
- * Analyze schema usage across the OpenAPI spec to determine if schemas
1977
- * are used in request, response, or both contexts
1787
+ * Initialize schema usage map using core utilities with operation filtering
1788
+ * This is a wrapper around core's analyzeSchemaUsage that adds operation filtering
1978
1789
  */
1979
- analyzeSchemaUsage() {
1980
- var _a, _b;
1981
- const requestSchemas = /* @__PURE__ */ new Set();
1982
- const responseSchemas = /* @__PURE__ */ new Set();
1983
- if (this.spec.paths) {
1790
+ initializeSchemaUsage() {
1791
+ var _a;
1792
+ if (this.options.operationFilters && this.spec.paths) {
1793
+ const requestSchemas = /* @__PURE__ */ new Set();
1794
+ const responseSchemas = /* @__PURE__ */ new Set();
1984
1795
  for (const [path, pathItem] of Object.entries(this.spec.paths)) {
1985
- const methods = ["get", "post", "put", "patch", "delete", "head", "options"];
1986
- for (const method of methods) {
1796
+ if (!isOpenAPIPathItem(pathItem)) continue;
1797
+ for (const method of HTTP_METHODS) {
1987
1798
  const operation = pathItem[method];
1988
- if (typeof operation !== "object" || !operation) continue;
1799
+ if (!operation) continue;
1989
1800
  this.filterStats.totalOperations++;
1990
1801
  if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters, this.filterStats)) {
1991
1802
  continue;
1992
1803
  }
1993
1804
  this.filterStats.includedOperations++;
1994
- if ("requestBody" in operation && operation.requestBody && typeof operation.requestBody === "object" && "content" in operation.requestBody && operation.requestBody.content) {
1995
- for (const mediaType of Object.values(operation.requestBody.content)) {
1996
- if (mediaType && typeof mediaType === "object" && "schema" in mediaType && mediaType.schema) {
1997
- this.extractSchemaRefs(mediaType.schema, requestSchemas);
1805
+ if (operation.requestBody && typeof operation.requestBody === "object") {
1806
+ const reqBody = operation.requestBody;
1807
+ if (reqBody.content && typeof reqBody.content === "object") {
1808
+ for (const mediaType of Object.values(reqBody.content)) {
1809
+ if (mediaType && typeof mediaType === "object" && "schema" in mediaType && mediaType.schema) {
1810
+ extractSchemaRefs(mediaType.schema, requestSchemas);
1811
+ }
1998
1812
  }
1999
1813
  }
2000
1814
  }
2001
- if ("responses" in operation && operation.responses && typeof operation.responses === "object") {
1815
+ if (operation.responses && typeof operation.responses === "object") {
2002
1816
  for (const response of Object.values(operation.responses)) {
2003
1817
  if (response && typeof response === "object" && "content" in response && response.content && typeof response.content === "object") {
2004
1818
  for (const mediaType of Object.values(response.content)) {
2005
1819
  if (mediaType && typeof mediaType === "object" && "schema" in mediaType && mediaType.schema) {
2006
- this.extractSchemaRefs(mediaType.schema, responseSchemas);
1820
+ extractSchemaRefs(mediaType.schema, responseSchemas);
2007
1821
  }
2008
1822
  }
2009
1823
  }
2010
1824
  }
2011
1825
  }
2012
- if ("parameters" in operation && Array.isArray(operation.parameters)) {
1826
+ if (operation.parameters && Array.isArray(operation.parameters)) {
2013
1827
  for (const param of operation.parameters) {
2014
- if (param && typeof param === "object" && "schema" in param && param.schema) {
2015
- this.extractSchemaRefs(param.schema, requestSchemas);
1828
+ if (isResolvedParameter(param) && param.schema) {
1829
+ extractSchemaRefs(param.schema, requestSchemas);
2016
1830
  }
2017
1831
  }
2018
1832
  }
2019
1833
  }
2020
1834
  }
2021
- this.expandTransitiveReferences(requestSchemas);
2022
- this.expandTransitiveReferences(responseSchemas);
2023
- }
2024
- if (!this.spec.paths || requestSchemas.size === 0 && responseSchemas.size === 0) {
2025
- for (const [name, schema] of Object.entries(((_a = this.spec.components) == null ? void 0 : _a.schemas) || {})) {
2026
- const hasReadOnly = this.hasReadOnlyProperties(schema);
2027
- const hasWriteOnly = this.hasWriteOnlyProperties(schema);
2028
- if (hasWriteOnly && !hasReadOnly) {
2029
- requestSchemas.add(name);
2030
- } else if (hasReadOnly && !hasWriteOnly) {
2031
- responseSchemas.add(name);
1835
+ expandTransitiveReferences(requestSchemas, this.spec);
1836
+ expandTransitiveReferences(responseSchemas, this.spec);
1837
+ for (const [name] of Object.entries(((_a = this.spec.components) == null ? void 0 : _a.schemas) || {})) {
1838
+ if (requestSchemas.has(name) && responseSchemas.has(name)) {
1839
+ this.schemaUsageMap.set(name, "both");
1840
+ } else if (requestSchemas.has(name)) {
1841
+ this.schemaUsageMap.set(name, "request");
1842
+ } else if (responseSchemas.has(name)) {
1843
+ this.schemaUsageMap.set(name, "response");
2032
1844
  }
2033
1845
  }
2034
- }
2035
- for (const [name] of Object.entries(((_b = this.spec.components) == null ? void 0 : _b.schemas) || {})) {
2036
- if (requestSchemas.has(name) && responseSchemas.has(name)) {
1846
+ const circularSchemas = detectCircularReferences(this.spec);
1847
+ for (const name of circularSchemas) {
2037
1848
  this.schemaUsageMap.set(name, "both");
2038
- } else if (requestSchemas.has(name)) {
2039
- this.schemaUsageMap.set(name, "request");
2040
- } else if (responseSchemas.has(name)) {
2041
- this.schemaUsageMap.set(name, "response");
2042
- }
2043
- }
2044
- this.detectCircularReferences();
2045
- }
2046
- /**
2047
- * Expand a set of schemas to include all transitively referenced schemas
2048
- */
2049
- expandTransitiveReferences(schemas) {
2050
- var _a, _b;
2051
- const toProcess = Array.from(schemas);
2052
- const processed = /* @__PURE__ */ new Set();
2053
- while (toProcess.length > 0) {
2054
- const schemaName = toProcess.pop();
2055
- if (!schemaName || processed.has(schemaName)) continue;
2056
- processed.add(schemaName);
2057
- const schema = (_b = (_a = this.spec.components) == null ? void 0 : _a.schemas) == null ? void 0 : _b[schemaName];
2058
- if (schema) {
2059
- const refs = /* @__PURE__ */ new Set();
2060
- this.extractSchemaRefs(schema, refs);
2061
- for (const ref of refs) {
2062
- if (!schemas.has(ref)) {
2063
- schemas.add(ref);
2064
- toProcess.push(ref);
2065
- }
2066
- }
2067
- }
2068
- }
2069
- }
2070
- /**
2071
- * Extract schema names from $ref and nested structures
2072
- */
2073
- extractSchemaRefs(schema, refs) {
2074
- if (!schema) return;
2075
- if (schema.$ref) {
2076
- const refName = resolveRef(schema.$ref);
2077
- refs.add(refName);
2078
- }
2079
- if (schema.allOf) {
2080
- for (const subSchema of schema.allOf) {
2081
- this.extractSchemaRefs(subSchema, refs);
2082
- }
2083
- }
2084
- if (schema.oneOf) {
2085
- for (const subSchema of schema.oneOf) {
2086
- this.extractSchemaRefs(subSchema, refs);
2087
- }
2088
- }
2089
- if (schema.anyOf) {
2090
- for (const subSchema of schema.anyOf) {
2091
- this.extractSchemaRefs(subSchema, refs);
2092
- }
2093
- }
2094
- if (schema.items) {
2095
- this.extractSchemaRefs(schema.items, refs);
2096
- }
2097
- if (schema.properties) {
2098
- for (const prop of Object.values(schema.properties)) {
2099
- this.extractSchemaRefs(prop, refs);
2100
- }
2101
- }
2102
- }
2103
- /**
2104
- * Check if schema has readOnly properties
2105
- */
2106
- hasReadOnlyProperties(schema) {
2107
- if (schema.readOnly) return true;
2108
- if (schema.properties) {
2109
- for (const prop of Object.values(schema.properties)) {
2110
- if (this.hasReadOnlyProperties(prop)) return true;
2111
1849
  }
2112
- }
2113
- return false;
2114
- }
2115
- /**
2116
- * Check if schema has writeOnly properties
2117
- */
2118
- hasWriteOnlyProperties(schema) {
2119
- if (schema.writeOnly) return true;
2120
- if (schema.properties) {
2121
- for (const prop of Object.values(schema.properties)) {
2122
- if (this.hasWriteOnlyProperties(prop)) return true;
2123
- }
2124
- }
2125
- return false;
2126
- }
2127
- /**
2128
- * Detect circular references and mark them as "both" context for safety
2129
- */
2130
- detectCircularReferences() {
2131
- var _a;
2132
- const visited = /* @__PURE__ */ new Set();
2133
- const recursionStack = /* @__PURE__ */ new Set();
2134
- const detectCycle = (name) => {
2135
- var _a2, _b;
2136
- if (recursionStack.has(name)) {
2137
- return true;
2138
- }
2139
- if (visited.has(name)) {
2140
- return false;
2141
- }
2142
- visited.add(name);
2143
- recursionStack.add(name);
2144
- const schema = (_b = (_a2 = this.spec.components) == null ? void 0 : _a2.schemas) == null ? void 0 : _b[name];
2145
- if (schema) {
2146
- const refs = /* @__PURE__ */ new Set();
2147
- this.extractSchemaRefs(schema, refs);
2148
- for (const ref of refs) {
2149
- if (detectCycle(ref)) {
2150
- this.schemaUsageMap.set(name, "both");
2151
- recursionStack.delete(name);
2152
- return true;
1850
+ } else {
1851
+ const analysis = analyzeSchemaUsage(this.spec);
1852
+ this.schemaUsageMap = analysis.usageMap;
1853
+ if (this.spec.paths) {
1854
+ for (const pathItem of Object.values(this.spec.paths)) {
1855
+ if (!isOpenAPIPathItem(pathItem)) continue;
1856
+ for (const method of HTTP_METHODS) {
1857
+ const operation = pathItem[method];
1858
+ if (typeof operation === "object" && operation) {
1859
+ this.filterStats.totalOperations++;
1860
+ this.filterStats.includedOperations++;
1861
+ }
2153
1862
  }
2154
1863
  }
2155
1864
  }
2156
- recursionStack.delete(name);
2157
- return false;
2158
- };
2159
- for (const name of Object.keys(((_a = this.spec.components) == null ? void 0 : _a.schemas) || {})) {
2160
- detectCycle(name);
2161
1865
  }
2162
1866
  }
2163
1867
  /**
@@ -2190,7 +1894,7 @@ var OpenApiGenerator = class {
2190
1894
  */
2191
1895
  validateSchemaRefs(schemaName, schema, allSchemas, path = "") {
2192
1896
  if (schema.$ref) {
2193
- const refName = resolveRef(schema.$ref);
1897
+ const refName = resolveRefName2(schema.$ref);
2194
1898
  if (!allSchemas.includes(refName)) {
2195
1899
  throw new SpecValidationError(
2196
1900
  `Invalid reference${path ? ` at '${path}'` : ""}: '${schema.$ref}' points to non-existent schema '${refName}'`,
@@ -2239,7 +1943,7 @@ var OpenApiGenerator = class {
2239
1943
  const resolvedOptions = context === "response" ? this.responseOptions : this.requestOptions;
2240
1944
  if (schema.enum) {
2241
1945
  const jsdoc2 = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
2242
- const strippedName2 = stripPrefix(name, this.options.stripSchemaPrefix);
1946
+ const strippedName2 = stripPrefix2(name, this.options.stripSchemaPrefix);
2243
1947
  const { schemaCode, typeCode } = generateEnum(strippedName2, schema.enum, {
2244
1948
  prefix: this.options.prefix,
2245
1949
  suffix: this.options.suffix
@@ -2249,11 +1953,11 @@ ${typeCode}`;
2249
1953
  this.schemas.set(name, enumSchemaCode);
2250
1954
  return;
2251
1955
  }
2252
- const strippedName = stripPrefix(name, this.options.stripSchemaPrefix);
2253
- const schemaName = `${toCamelCase(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
2254
- const jsdoc = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
1956
+ const strippedName = stripPrefix2(name, this.options.stripSchemaPrefix);
1957
+ const schemaName = `${toCamelCase3(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
1958
+ let jsdoc = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
2255
1959
  if (schema.allOf && schema.allOf.length === 1 && schema.allOf[0].$ref) {
2256
- const refName = resolveRef(schema.allOf[0].$ref);
1960
+ const refName = resolveRefName2(schema.allOf[0].$ref);
2257
1961
  (_a = this.schemaDependencies.get(name)) == null ? void 0 : _a.add(refName);
2258
1962
  }
2259
1963
  this.propertyGenerator = new PropertyGenerator({
@@ -2271,9 +1975,26 @@ ${typeCode}`;
2271
1975
  },
2272
1976
  stripSchemaPrefix: this.options.stripSchemaPrefix,
2273
1977
  dateTimeValidation: this.dateTimeValidation,
2274
- patternCache: this.patternCache
1978
+ patternCache: this.patternCache,
1979
+ separateTypesFile: this.separateSchemasMode
2275
1980
  });
1981
+ this.propertyGenerator.setCircularDependencies(this.circularDependencies);
1982
+ this.propertyGenerator.clearAllOfConflicts();
2276
1983
  const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, true);
1984
+ const allOfConflicts = this.propertyGenerator.getAllOfConflicts();
1985
+ if (allOfConflicts.length > 0) {
1986
+ this.allOfConflictCount += allOfConflicts.length;
1987
+ const conflictWarning = this.generateConflictJSDoc(allOfConflicts);
1988
+ if (jsdoc) {
1989
+ jsdoc = jsdoc.replace(/ \*\/\n$/, `
1990
+ ${conflictWarning} */
1991
+ `);
1992
+ } else {
1993
+ jsdoc = `/**
1994
+ ${conflictWarning} */
1995
+ `;
1996
+ }
1997
+ }
2277
1998
  const zodSchemaCode = `${jsdoc}export const ${schemaName} = ${zodSchema};`;
2278
1999
  if (zodSchema.includes("z.discriminatedUnion(")) {
2279
2000
  const match = zodSchema.match(/z\.discriminatedUnion\([^,]+,\s*\[([^\]]+)\]/);
@@ -2299,9 +2020,8 @@ ${typeCode}`;
2299
2020
  return;
2300
2021
  }
2301
2022
  for (const [path, pathItem] of Object.entries(this.spec.paths)) {
2302
- if (!pathItem || typeof pathItem !== "object") continue;
2303
- const methods = ["get", "post", "put", "patch", "delete", "head", "options"];
2304
- for (const method of methods) {
2023
+ if (!isOpenAPIPathItem(pathItem)) continue;
2024
+ for (const method of HTTP_METHODS) {
2305
2025
  const operation = pathItem[method];
2306
2026
  if (!operation) continue;
2307
2027
  if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
@@ -2309,18 +2029,18 @@ ${typeCode}`;
2309
2029
  }
2310
2030
  const allParams = mergeParameters(pathItem.parameters, operation.parameters, this.spec);
2311
2031
  const queryParams = allParams.filter(
2312
- (param) => param && typeof param === "object" && param.in === "query"
2032
+ (param) => isResolvedParameter(param) && param.in === "query"
2313
2033
  );
2314
2034
  if (queryParams.length === 0) {
2315
2035
  continue;
2316
2036
  }
2317
- let pascalOperationId;
2318
- if (operation.operationId) {
2319
- pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
2320
- } else {
2321
- const strippedPath = stripPathPrefix(path, this.options.stripPathPrefix);
2322
- pascalOperationId = this.generateMethodNameFromPath(method, strippedPath);
2323
- }
2037
+ const strippedPath = stripPathPrefix(path, this.options.stripPathPrefix);
2038
+ const pascalOperationId = getOperationName(
2039
+ operation.operationId,
2040
+ method,
2041
+ strippedPath,
2042
+ this.options.useOperationId
2043
+ );
2324
2044
  const schemaName = `${pascalOperationId}QueryParams`;
2325
2045
  if (!this.schemaDependencies.has(schemaName)) {
2326
2046
  this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
@@ -2350,7 +2070,7 @@ ${typeCode}`;
2350
2070
  required.push(paramName);
2351
2071
  }
2352
2072
  if (paramSchema.$ref) {
2353
- const refName = resolveRef(paramSchema.$ref);
2073
+ const refName = resolveRefName2(paramSchema.$ref);
2354
2074
  (_a = this.schemaDependencies.get(schemaName)) == null ? void 0 : _a.add(refName);
2355
2075
  }
2356
2076
  }
@@ -2365,9 +2085,7 @@ ${typeCode}`;
2365
2085
  ${propsCode}
2366
2086
  })`;
2367
2087
  const operationName = pascalOperationId;
2368
- const prefixedName = this.options.prefix ? `${toPascalCase(this.options.prefix)}${operationName}` : operationName;
2369
- const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
2370
- const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}QueryParamsSchema`;
2088
+ const camelCaseSchemaName = `${toCamelCase3(operationName, { prefix: this.options.prefix, suffix: this.options.suffix })}QueryParamsSchema`;
2371
2089
  const jsdocOperationName = operation.operationId || `${method.toUpperCase()} ${path}`;
2372
2090
  const jsdoc = `/**
2373
2091
  * Query parameters for ${jsdocOperationName}
@@ -2379,35 +2097,6 @@ ${propsCode}
2379
2097
  }
2380
2098
  }
2381
2099
  }
2382
- /**
2383
- * Generate a PascalCase method name from HTTP method and path
2384
- * Used as fallback when operationId is not available
2385
- * @internal
2386
- */
2387
- generateMethodNameFromPath(method, path) {
2388
- const segments = path.split("/").filter(Boolean).map((segment) => {
2389
- if (segment.startsWith("{") && segment.endsWith("}")) {
2390
- const paramName = segment.slice(1, -1);
2391
- return `By${this.capitalizeSegment(paramName)}`;
2392
- }
2393
- return this.capitalizeSegment(segment);
2394
- }).join("");
2395
- const capitalizedMethod = method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();
2396
- return `${capitalizedMethod}${segments}`;
2397
- }
2398
- /**
2399
- * Capitalizes a path segment, handling special characters like dashes, underscores, and dots
2400
- * @internal
2401
- */
2402
- capitalizeSegment(str) {
2403
- if (str.includes("-") || str.includes("_") || str.includes(".")) {
2404
- return str.split(/[-_.]/).map((part) => {
2405
- if (!part) return "";
2406
- return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
2407
- }).join("");
2408
- }
2409
- return str.charAt(0).toUpperCase() + str.slice(1);
2410
- }
2411
2100
  /**
2412
2101
  * Check if a header should be ignored based on filter patterns
2413
2102
  * @internal
@@ -2423,7 +2112,7 @@ ${propsCode}
2423
2112
  const headerLower = headerName.toLowerCase();
2424
2113
  return ignorePatterns.some((pattern) => {
2425
2114
  const patternLower = pattern.toLowerCase();
2426
- return minimatch3(headerLower, patternLower);
2115
+ return minimatch(headerLower, patternLower);
2427
2116
  });
2428
2117
  }
2429
2118
  /**
@@ -2436,9 +2125,8 @@ ${propsCode}
2436
2125
  return;
2437
2126
  }
2438
2127
  for (const [path, pathItem] of Object.entries(this.spec.paths)) {
2439
- if (!pathItem || typeof pathItem !== "object") continue;
2440
- const methods = ["get", "post", "put", "patch", "delete", "head", "options"];
2441
- for (const method of methods) {
2128
+ if (!isOpenAPIPathItem(pathItem)) continue;
2129
+ for (const method of HTTP_METHODS) {
2442
2130
  const operation = pathItem[method];
2443
2131
  if (!operation) continue;
2444
2132
  if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
@@ -2446,18 +2134,18 @@ ${propsCode}
2446
2134
  }
2447
2135
  const allParams = mergeParameters(pathItem.parameters, operation.parameters, this.spec);
2448
2136
  const headerParams = allParams.filter(
2449
- (param) => param && typeof param === "object" && param.in === "header" && !this.shouldIgnoreHeader(param.name)
2137
+ (param) => isResolvedParameter(param) && param.in === "header" && !this.shouldIgnoreHeader(param.name)
2450
2138
  );
2451
2139
  if (headerParams.length === 0) {
2452
2140
  continue;
2453
2141
  }
2454
- let pascalOperationId;
2455
- if (operation.operationId) {
2456
- pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
2457
- } else {
2458
- const strippedPath = stripPathPrefix(path, this.options.stripPathPrefix);
2459
- pascalOperationId = this.generateMethodNameFromPath(method, strippedPath);
2460
- }
2142
+ const strippedPath = stripPathPrefix(path, this.options.stripPathPrefix);
2143
+ const pascalOperationId = getOperationName(
2144
+ operation.operationId,
2145
+ method,
2146
+ strippedPath,
2147
+ this.options.useOperationId
2148
+ );
2461
2149
  const schemaName = `${pascalOperationId}HeaderParams`;
2462
2150
  if (!this.schemaDependencies.has(schemaName)) {
2463
2151
  this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
@@ -2476,7 +2164,7 @@ ${propsCode}
2476
2164
  zodType = `${zodType}.optional()`;
2477
2165
  properties[paramName] = zodType;
2478
2166
  if (paramSchema.$ref) {
2479
- const refName = resolveRef(paramSchema.$ref);
2167
+ const refName = resolveRefName2(paramSchema.$ref);
2480
2168
  (_a = this.schemaDependencies.get(schemaName)) == null ? void 0 : _a.add(refName);
2481
2169
  }
2482
2170
  }
@@ -2491,9 +2179,7 @@ ${propsCode}
2491
2179
  ${propsCode}
2492
2180
  })`;
2493
2181
  const operationName = pascalOperationId;
2494
- const prefixedName = this.options.prefix ? `${toPascalCase(this.options.prefix)}${operationName}` : operationName;
2495
- const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
2496
- const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}HeaderParamsSchema`;
2182
+ const camelCaseSchemaName = `${toCamelCase3(operationName, { prefix: this.options.prefix, suffix: this.options.suffix })}HeaderParamsSchema`;
2497
2183
  const jsdocOperationName = operation.operationId || `${method.toUpperCase()} ${path}`;
2498
2184
  const jsdoc = `/**
2499
2185
  * Header parameters for ${jsdocOperationName}
@@ -2510,9 +2196,9 @@ ${propsCode}
2510
2196
  */
2511
2197
  generateQueryParamType(schema, param) {
2512
2198
  if (schema.$ref) {
2513
- const refName = resolveRef(schema.$ref);
2514
- const strippedRefName = stripPrefix(refName, this.options.stripSchemaPrefix);
2515
- const schemaName = toCamelCase(strippedRefName, { prefix: this.options.prefix, suffix: this.options.suffix });
2199
+ const refName = resolveRefName2(schema.$ref);
2200
+ const strippedRefName = stripPrefix2(refName, this.options.stripSchemaPrefix);
2201
+ const schemaName = toCamelCase3(strippedRefName, { prefix: this.options.prefix, suffix: this.options.suffix });
2516
2202
  return `${schemaName}Schema`;
2517
2203
  }
2518
2204
  if (schema.enum) {
@@ -2529,7 +2215,7 @@ ${propsCode}
2529
2215
  if (typeof v === "string") {
2530
2216
  return `z.literal("${v}")`;
2531
2217
  }
2532
- return `z.literal(${v})`;
2218
+ return `z.literal(${String(v)})`;
2533
2219
  }).join(", ");
2534
2220
  return `z.union([${literalValues}])`;
2535
2221
  }
@@ -2609,17 +2295,23 @@ ${propsCode}
2609
2295
  return;
2610
2296
  }
2611
2297
  const deps = this.schemaDependencies.get(name);
2298
+ let dependsOnCircular = false;
2612
2299
  if (deps && deps.size > 0) {
2613
2300
  for (const dep of deps) {
2614
2301
  if (this.schemas.has(dep) || this.types.has(dep)) {
2615
2302
  visit(dep);
2303
+ if (circularDeps.has(dep)) {
2304
+ dependsOnCircular = true;
2305
+ }
2616
2306
  }
2617
2307
  }
2618
2308
  }
2619
2309
  visiting.delete(name);
2620
2310
  visited.add(name);
2621
- if (!circularDeps.has(name)) {
2311
+ if (!circularDeps.has(name) && !dependsOnCircular) {
2622
2312
  sorted.push(name);
2313
+ } else if (dependsOnCircular && !circularDeps.has(name)) {
2314
+ circularDeps.add(name);
2623
2315
  }
2624
2316
  };
2625
2317
  const allNames = /* @__PURE__ */ new Set([...this.schemas.keys(), ...this.types.keys()]);
@@ -2627,9 +2319,8 @@ ${propsCode}
2627
2319
  visit(name);
2628
2320
  }
2629
2321
  for (const name of circularDeps) {
2630
- if (!visited.has(name)) {
2322
+ if (!sorted.includes(name)) {
2631
2323
  sorted.push(name);
2632
- visited.add(name);
2633
2324
  }
2634
2325
  }
2635
2326
  return [...sorted, ...aliases];
@@ -2656,7 +2347,8 @@ ${propsCode}
2656
2347
  `// Total schemas: ${stats.totalSchemas}`,
2657
2348
  `// Circular references: ${stats.withCircularRefs}`,
2658
2349
  `// Discriminated unions: ${stats.withDiscriminators}`,
2659
- `// With constraints: ${stats.withConstraints}`
2350
+ `// With constraints: ${stats.withConstraints}`,
2351
+ `// AllOf conflicts: ${this.allOfConflictCount}`
2660
2352
  ];
2661
2353
  if (this.options.operationFilters && this.filterStats.totalOperations > 0) {
2662
2354
  output.push("//");
@@ -2668,6 +2360,106 @@ ${propsCode}
2668
2360
  output.push(`// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`);
2669
2361
  return output;
2670
2362
  }
2363
+ /**
2364
+ * Pre-analyze schemas to detect circular dependencies before code generation.
2365
+ * This allows the property generator to use z.lazy() for forward references.
2366
+ */
2367
+ analyzeCircularDependencies() {
2368
+ var _a;
2369
+ if (!((_a = this.spec.components) == null ? void 0 : _a.schemas)) return;
2370
+ const dependencies = /* @__PURE__ */ new Map();
2371
+ const collectDependencies = (name, schema, visited2 = /* @__PURE__ */ new Set()) => {
2372
+ if (visited2.has(name)) return /* @__PURE__ */ new Set();
2373
+ visited2.add(name);
2374
+ const deps = /* @__PURE__ */ new Set();
2375
+ if (schema.$ref) {
2376
+ const refName = resolveRefName2(schema.$ref);
2377
+ deps.add(refName);
2378
+ }
2379
+ if (schema.allOf) {
2380
+ for (const item of schema.allOf) {
2381
+ const itemDeps = collectDependencies(`${name}_allOf`, item, new Set(visited2));
2382
+ for (const dep of itemDeps) deps.add(dep);
2383
+ }
2384
+ }
2385
+ if (schema.oneOf) {
2386
+ for (const item of schema.oneOf) {
2387
+ const itemDeps = collectDependencies(`${name}_oneOf`, item, new Set(visited2));
2388
+ for (const dep of itemDeps) deps.add(dep);
2389
+ }
2390
+ }
2391
+ if (schema.anyOf) {
2392
+ for (const item of schema.anyOf) {
2393
+ const itemDeps = collectDependencies(`${name}_anyOf`, item, new Set(visited2));
2394
+ for (const dep of itemDeps) deps.add(dep);
2395
+ }
2396
+ }
2397
+ if (schema.properties) {
2398
+ for (const propSchema of Object.values(schema.properties)) {
2399
+ const propDeps = collectDependencies(`${name}_prop`, propSchema, new Set(visited2));
2400
+ for (const dep of propDeps) deps.add(dep);
2401
+ }
2402
+ }
2403
+ if (schema.items) {
2404
+ const itemDeps = collectDependencies(`${name}_items`, schema.items, new Set(visited2));
2405
+ for (const dep of itemDeps) deps.add(dep);
2406
+ }
2407
+ if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
2408
+ const addDeps = collectDependencies(`${name}_additional`, schema.additionalProperties, new Set(visited2));
2409
+ for (const dep of addDeps) deps.add(dep);
2410
+ }
2411
+ return deps;
2412
+ };
2413
+ for (const [name, schema] of Object.entries(this.spec.components.schemas)) {
2414
+ if (this.options.operationFilters && this.schemaUsageMap.size > 0 && !this.schemaUsageMap.has(name)) {
2415
+ continue;
2416
+ }
2417
+ dependencies.set(name, collectDependencies(name, schema));
2418
+ }
2419
+ const visited = /* @__PURE__ */ new Set();
2420
+ const visiting = /* @__PURE__ */ new Set();
2421
+ const detectCircular = (name, path = []) => {
2422
+ if (visited.has(name)) return;
2423
+ if (visiting.has(name)) {
2424
+ const cycleStart = path.indexOf(name);
2425
+ if (cycleStart >= 0) {
2426
+ for (let i = cycleStart; i < path.length; i++) {
2427
+ this.circularDependencies.add(path[i]);
2428
+ }
2429
+ }
2430
+ this.circularDependencies.add(name);
2431
+ return;
2432
+ }
2433
+ visiting.add(name);
2434
+ path.push(name);
2435
+ const deps = dependencies.get(name);
2436
+ if (deps) {
2437
+ for (const dep of deps) {
2438
+ if (dependencies.has(dep)) {
2439
+ detectCircular(dep, [...path]);
2440
+ }
2441
+ }
2442
+ }
2443
+ visiting.delete(name);
2444
+ visited.add(name);
2445
+ };
2446
+ for (const name of dependencies.keys()) {
2447
+ detectCircular(name, []);
2448
+ }
2449
+ }
2450
+ /**
2451
+ * Generate JSDoc warning for allOf conflicts
2452
+ * @param conflicts Array of conflict description strings
2453
+ * @returns JSDoc formatted warning string
2454
+ */
2455
+ generateConflictJSDoc(conflicts) {
2456
+ const lines = [" * @warning allOf property conflicts detected:"];
2457
+ for (const conflict of conflicts) {
2458
+ lines.push(` * - ${conflict}`);
2459
+ }
2460
+ return `${lines.join("\n")}
2461
+ `;
2462
+ }
2671
2463
  };
2672
2464
 
2673
2465
  // src/types.ts
@@ -2681,8 +2473,10 @@ export {
2681
2473
  FileOperationError,
2682
2474
  GeneratorError,
2683
2475
  OpenApiGenerator,
2684
- SchemaGenerationError,
2685
- SpecValidationError,
2476
+ PropertyGenerator,
2477
+ SchemaGenerationError2 as SchemaGenerationError,
2478
+ SpecValidationError2 as SpecValidationError,
2479
+ buildDateTimeValidation,
2686
2480
  defineConfig
2687
2481
  };
2688
2482
  //# sourceMappingURL=index.mjs.map