@cerios/openapi-to-zod 1.2.0 → 1.3.1

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
@@ -86,12 +86,12 @@ function toCamelCase(str, options) {
86
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
87
  }
88
88
  if (options == null ? void 0 : options.prefix) {
89
- const prefix = options.prefix.toLowerCase();
89
+ const prefix = options.prefix.charAt(0).toLowerCase() + options.prefix.slice(1);
90
90
  name = prefix + name.charAt(0).toUpperCase() + name.slice(1);
91
91
  }
92
92
  if (options == null ? void 0 : options.suffix) {
93
- const suffix = options.suffix;
94
- name = name + suffix.charAt(0).toUpperCase() + suffix.slice(1).toLowerCase();
93
+ const suffix = options.suffix.charAt(0).toUpperCase() + options.suffix.slice(1);
94
+ name = name + suffix;
95
95
  }
96
96
  return name;
97
97
  }
@@ -403,12 +403,56 @@ function generateArrayValidation(schema, context) {
403
403
  }
404
404
 
405
405
  // src/validators/composition-validator.ts
406
+ function isDiscriminatorRequired(schemas, discriminator, context) {
407
+ const invalidSchemas = [];
408
+ for (const schema of schemas) {
409
+ const resolved = resolveSchema(schema, context);
410
+ const required = resolved.required || [];
411
+ if (!required.includes(discriminator)) {
412
+ const schemaName = schema.$ref ? schema.$ref.split("/").pop() || "inline" : "inline";
413
+ invalidSchemas.push(schemaName);
414
+ }
415
+ }
416
+ return {
417
+ valid: invalidSchemas.length === 0,
418
+ invalidSchemas
419
+ };
420
+ }
406
421
  function generateUnion(schemas, discriminator, isNullable2, context, options, currentSchema) {
422
+ if (schemas.length === 0) {
423
+ console.warn(
424
+ "[openapi-to-zod] Warning: Empty oneOf/anyOf array encountered. This is likely a malformed OpenAPI spec. Generating z.never() as fallback."
425
+ );
426
+ return wrapNullable(
427
+ 'z.never().describe("Empty oneOf/anyOf in OpenAPI spec - no valid schema defined")',
428
+ isNullable2
429
+ );
430
+ }
431
+ if (schemas.length === 1) {
432
+ let singleSchema = context.generatePropertySchema(schemas[0], currentSchema);
433
+ if ((options == null ? void 0 : options.passthrough) && !singleSchema.includes(".catchall(")) {
434
+ singleSchema = `${singleSchema}.catchall(z.unknown())`;
435
+ }
436
+ return wrapNullable(singleSchema, isNullable2);
437
+ }
407
438
  if (discriminator) {
408
439
  let resolvedSchemas = schemas;
409
440
  if ((options == null ? void 0 : options.discriminatorMapping) && context.resolveDiscriminatorMapping) {
410
441
  resolvedSchemas = context.resolveDiscriminatorMapping(options.discriminatorMapping, schemas);
411
442
  }
443
+ const discriminatorCheck = isDiscriminatorRequired(resolvedSchemas, discriminator, context);
444
+ if (!discriminatorCheck.valid) {
445
+ console.warn(
446
+ `[openapi-to-zod] Warning: Discriminator "${discriminator}" is not required in schemas: ${discriminatorCheck.invalidSchemas.join(", ")}. Falling back to z.union() instead of z.discriminatedUnion().`
447
+ );
448
+ let schemaStrings3 = resolvedSchemas.map((s) => context.generatePropertySchema(s, currentSchema));
449
+ if (options == null ? void 0 : options.passthrough) {
450
+ schemaStrings3 = schemaStrings3.map((s) => s.includes(".catchall(") ? s : `${s}.catchall(z.unknown())`);
451
+ }
452
+ const fallbackDescription = `Discriminator "${discriminator}" is optional in some schemas (${discriminatorCheck.invalidSchemas.join(", ")}), using z.union() instead of z.discriminatedUnion()`;
453
+ const union3 = `z.union([${schemaStrings3.join(", ")}]).describe("${fallbackDescription}")`;
454
+ return wrapNullable(union3, isNullable2);
455
+ }
412
456
  let schemaStrings2 = resolvedSchemas.map((s) => context.generatePropertySchema(s, currentSchema));
413
457
  if (options == null ? void 0 : options.passthrough) {
414
458
  schemaStrings2 = schemaStrings2.map((s) => s.includes(".catchall(") ? s : `${s}.catchall(z.unknown())`);
@@ -423,25 +467,102 @@ function generateUnion(schemas, discriminator, isNullable2, context, options, cu
423
467
  const union = `z.union([${schemaStrings.join(", ")}])`;
424
468
  return wrapNullable(union, isNullable2);
425
469
  }
426
- function generateAllOf(schemas, isNullable2, context, currentSchema) {
470
+ function resolveSchema(schema, context) {
471
+ if (schema.$ref && context.resolveSchemaRef) {
472
+ const resolved = context.resolveSchemaRef(schema.$ref);
473
+ if (resolved) {
474
+ return resolved;
475
+ }
476
+ }
477
+ return schema;
478
+ }
479
+ function collectProperties(schema, context) {
480
+ const resolved = resolveSchema(schema, context);
481
+ const props = /* @__PURE__ */ new Map();
482
+ const sourceName = schema.$ref ? schema.$ref.split("/").pop() || "unknown" : "inline";
483
+ if (resolved.properties) {
484
+ for (const [key, value] of Object.entries(resolved.properties)) {
485
+ props.set(key, { schema: value, source: sourceName });
486
+ }
487
+ }
488
+ if (resolved.allOf) {
489
+ for (const subSchema of resolved.allOf) {
490
+ const subProps = collectProperties(subSchema, context);
491
+ for (const [key, value] of subProps) {
492
+ if (!props.has(key)) {
493
+ props.set(key, value);
494
+ }
495
+ }
496
+ }
497
+ }
498
+ return props;
499
+ }
500
+ function schemasMatch(a, b) {
501
+ return JSON.stringify(a) === JSON.stringify(b);
502
+ }
503
+ function detectConflictingProperties(schemas, context) {
504
+ const conflicts = [];
505
+ const propertyMap = /* @__PURE__ */ new Map();
506
+ for (const schema of schemas) {
507
+ const schemaProps = collectProperties(schema, context);
508
+ for (const [propName, propInfo] of schemaProps) {
509
+ const existing = propertyMap.get(propName);
510
+ if (existing) {
511
+ if (!schemasMatch(existing.schema, propInfo.schema)) {
512
+ conflicts.push(
513
+ `Property "${propName}" has conflicting definitions in ${existing.source} and ${propInfo.source}`
514
+ );
515
+ }
516
+ } else {
517
+ propertyMap.set(propName, propInfo);
518
+ }
519
+ }
520
+ }
521
+ return conflicts;
522
+ }
523
+ function generateAllOf(schemas, isNullable2, context, currentSchema, explicitNullableFalse = false) {
427
524
  if (schemas.length === 1) {
428
- const singleSchema = context.generatePropertySchema(schemas[0], currentSchema, false);
525
+ const singleSchema = context.generatePropertySchema(schemas[0], currentSchema, false, explicitNullableFalse);
429
526
  return wrapNullable(singleSchema, isNullable2);
430
527
  }
528
+ const conflicts = detectConflictingProperties(schemas, context);
529
+ let conflictDescription = "";
530
+ if (conflicts.length > 0) {
531
+ for (const conflict of conflicts) {
532
+ console.warn(`[openapi-to-zod] Warning: allOf composition conflict - ${conflict}`);
533
+ }
534
+ conflictDescription = `allOf property conflicts detected: ${conflicts.join("; ")}`;
535
+ }
431
536
  const allObjects = schemas.every((s) => s.type === "object" || s.properties || s.$ref || s.allOf);
432
- const schemaStrings = schemas.map((s) => context.generatePropertySchema(s, currentSchema, false));
537
+ let result;
433
538
  if (allObjects) {
434
- let merged2 = schemaStrings[0];
539
+ let merged = context.generatePropertySchema(schemas[0], currentSchema, false);
540
+ for (let i = 1; i < schemas.length; i++) {
541
+ const schema = schemas[i];
542
+ if (schema.$ref) {
543
+ const refSchema = context.generatePropertySchema(schema, currentSchema, false);
544
+ merged = `${merged}.extend(${refSchema}.shape)`;
545
+ } else if (context.generateInlineObjectShape && (schema.properties || schema.type === "object")) {
546
+ const inlineShape = context.generateInlineObjectShape(schema, currentSchema);
547
+ merged = `${merged}.extend(${inlineShape})`;
548
+ } else {
549
+ const schemaString = context.generatePropertySchema(schema, currentSchema, false);
550
+ merged = `${merged}.extend(${schemaString}.shape)`;
551
+ }
552
+ }
553
+ result = merged;
554
+ } else {
555
+ const schemaStrings = schemas.map((s) => context.generatePropertySchema(s, currentSchema, false));
556
+ let merged = schemaStrings[0];
435
557
  for (let i = 1; i < schemaStrings.length; i++) {
436
- merged2 = `${merged2}.merge(${schemaStrings[i]})`;
558
+ merged = `${merged}.and(${schemaStrings[i]})`;
437
559
  }
438
- return wrapNullable(merged2, isNullable2);
560
+ result = merged;
439
561
  }
440
- let merged = schemaStrings[0];
441
- for (let i = 1; i < schemaStrings.length; i++) {
442
- merged = `${merged}.and(${schemaStrings[i]})`;
562
+ if (conflictDescription) {
563
+ result = `${result}.describe("${conflictDescription}")`;
443
564
  }
444
- return wrapNullable(merged, isNullable2);
565
+ return wrapNullable(result, isNullable2);
445
566
  }
446
567
 
447
568
  // src/validators/number-validator.ts
@@ -1117,6 +1238,15 @@ var _PropertyGenerator = class _PropertyGenerator {
1117
1238
  }
1118
1239
  return mappedSchemas;
1119
1240
  }
1241
+ /**
1242
+ * Resolve a $ref string to the actual schema
1243
+ */
1244
+ resolveSchemaRef(ref) {
1245
+ var _a, _b;
1246
+ const schemaName = ref.split("/").pop();
1247
+ if (!schemaName) return void 0;
1248
+ return (_b = (_a = this.context.spec.components) == null ? void 0 : _a.schemas) == null ? void 0 : _b[schemaName];
1249
+ }
1120
1250
  /**
1121
1251
  * Resolve a schema name through any aliases to get the actual schema name
1122
1252
  * If the schema is an alias (allOf with single $ref), return the target name
@@ -1194,7 +1324,7 @@ var _PropertyGenerator = class _PropertyGenerator {
1194
1324
  let schemaWithCatchall = baseSchema;
1195
1325
  if (baseSchema.includes(".union([") || baseSchema.includes(".discriminatedUnion(")) {
1196
1326
  schemaWithCatchall = baseSchema;
1197
- } else if (baseSchema.includes(".merge(")) {
1327
+ } else if (baseSchema.includes(".extend(")) {
1198
1328
  schemaWithCatchall = `${baseSchema}.catchall(z.unknown())`;
1199
1329
  }
1200
1330
  if (schema.unevaluatedProperties === false) {
@@ -1207,12 +1337,21 @@ var _PropertyGenerator = class _PropertyGenerator {
1207
1337
  }
1208
1338
  /**
1209
1339
  * Generate Zod schema for a property
1340
+ * @param schema - The OpenAPI schema to generate
1341
+ * @param currentSchema - The name of the current schema being processed (for circular ref detection)
1342
+ * @param isTopLevel - Whether this is a top-level schema definition
1343
+ * @param suppressDefaultNullable - When true, don't apply defaultNullable (used when outer schema has explicit nullable: false)
1210
1344
  */
1211
- generatePropertySchema(schema, currentSchema, isTopLevel = false) {
1345
+ generatePropertySchema(schema, currentSchema, isTopLevel = false, suppressDefaultNullable = false) {
1212
1346
  var _a, _b, _c, _d, _e;
1213
1347
  const isCacheable = !schema.$ref && !schema.allOf && !schema.oneOf && !schema.anyOf && !currentSchema;
1214
1348
  if (isCacheable) {
1215
- const cacheKey = JSON.stringify({ schema, type: this.context.schemaType, mode: this.context.mode });
1349
+ const cacheKey = JSON.stringify({
1350
+ schema,
1351
+ type: this.context.schemaType,
1352
+ mode: this.context.mode,
1353
+ suppressDefaultNullable
1354
+ });
1216
1355
  const cached = this.schemaCache.get(cacheKey);
1217
1356
  if (cached) {
1218
1357
  return cached;
@@ -1221,7 +1360,10 @@ var _PropertyGenerator = class _PropertyGenerator {
1221
1360
  if ((this.context.schemaType === "request" || this.context.schemaType === "response") && schema.properties) {
1222
1361
  schema = this.filterNestedProperties(schema);
1223
1362
  }
1224
- const effectiveDefaultNullable = isTopLevel ? false : this.context.defaultNullable;
1363
+ const isEnum = !!schema.enum;
1364
+ const isConst = schema.const !== void 0;
1365
+ const shouldApplyDefaultNullable = !isTopLevel && !isEnum && !isConst && !suppressDefaultNullable;
1366
+ const effectiveDefaultNullable = shouldApplyDefaultNullable ? this.context.defaultNullable : false;
1225
1367
  const nullable = isNullable(schema, effectiveDefaultNullable);
1226
1368
  if (hasMultipleTypes(schema)) {
1227
1369
  const union = this.generateMultiTypeUnion(schema, currentSchema);
@@ -1271,11 +1413,17 @@ var _PropertyGenerator = class _PropertyGenerator {
1271
1413
  return wrapNullable(zodUnion, nullable);
1272
1414
  }
1273
1415
  if (schema.allOf) {
1416
+ const explicitNullableFalse = schema.nullable === false;
1274
1417
  let composition = generateAllOf(
1275
1418
  schema.allOf,
1276
1419
  nullable,
1277
- { generatePropertySchema: this.generatePropertySchema.bind(this) },
1278
- currentSchema
1420
+ {
1421
+ generatePropertySchema: this.generatePropertySchema.bind(this),
1422
+ generateInlineObjectShape: this.generateInlineObjectShape.bind(this),
1423
+ resolveSchemaRef: this.resolveSchemaRef.bind(this)
1424
+ },
1425
+ currentSchema,
1426
+ explicitNullableFalse
1279
1427
  );
1280
1428
  if (schema.unevaluatedProperties !== void 0) {
1281
1429
  composition = this.applyUnevaluatedProperties(composition, schema);
@@ -1290,7 +1438,8 @@ var _PropertyGenerator = class _PropertyGenerator {
1290
1438
  nullable,
1291
1439
  {
1292
1440
  generatePropertySchema: this.generatePropertySchema.bind(this),
1293
- resolveDiscriminatorMapping: this.resolveDiscriminatorMapping.bind(this)
1441
+ resolveDiscriminatorMapping: this.resolveDiscriminatorMapping.bind(this),
1442
+ resolveSchemaRef: this.resolveSchemaRef.bind(this)
1294
1443
  },
1295
1444
  {
1296
1445
  passthrough: needsPassthrough,
@@ -1311,7 +1460,8 @@ var _PropertyGenerator = class _PropertyGenerator {
1311
1460
  nullable,
1312
1461
  {
1313
1462
  generatePropertySchema: this.generatePropertySchema.bind(this),
1314
- resolveDiscriminatorMapping: this.resolveDiscriminatorMapping.bind(this)
1463
+ resolveDiscriminatorMapping: this.resolveDiscriminatorMapping.bind(this),
1464
+ resolveSchemaRef: this.resolveSchemaRef.bind(this)
1315
1465
  },
1316
1466
  {
1317
1467
  passthrough: needsPassthrough,
@@ -1374,7 +1524,17 @@ var _PropertyGenerator = class _PropertyGenerator {
1374
1524
  );
1375
1525
  validation = addDescription(validation, schema.description, this.context.useDescribe);
1376
1526
  } else {
1377
- validation = "z.record(z.string(), z.unknown())";
1527
+ switch (this.context.emptyObjectBehavior) {
1528
+ case "strict":
1529
+ validation = "z.strictObject({})";
1530
+ break;
1531
+ case "loose":
1532
+ validation = "z.looseObject({})";
1533
+ break;
1534
+ default:
1535
+ validation = "z.record(z.string(), z.unknown())";
1536
+ break;
1537
+ }
1378
1538
  validation = addDescription(validation, schema.description, this.context.useDescribe);
1379
1539
  }
1380
1540
  break;
@@ -1389,6 +1549,44 @@ var _PropertyGenerator = class _PropertyGenerator {
1389
1549
  }
1390
1550
  return result;
1391
1551
  }
1552
+ /**
1553
+ * Generate inline object shape for use with .extend()
1554
+ * Returns just the shape object literal: { prop1: z.string(), prop2: z.number() }
1555
+ *
1556
+ * This method is specifically for allOf compositions where we need to pass
1557
+ * the shape directly to .extend() instead of using z.object({...}).shape.
1558
+ * This avoids the .nullable().shape bug when inline objects have nullable: true.
1559
+ *
1560
+ * According to Zod docs (https://zod.dev/api?id=extend):
1561
+ * - .extend() accepts an object of shape definitions
1562
+ * - e.g., baseSchema.extend({ prop: z.string() })
1563
+ */
1564
+ generateInlineObjectShape(schema, currentSchema) {
1565
+ const required = new Set(schema.required || []);
1566
+ const properties = [];
1567
+ if (schema.properties) {
1568
+ for (const [propName, propSchema] of Object.entries(schema.properties)) {
1569
+ if (!this.shouldIncludeProperty(propSchema)) {
1570
+ continue;
1571
+ }
1572
+ const isRequired = required.has(propName);
1573
+ const zodSchema = this.generatePropertySchema(propSchema, currentSchema);
1574
+ const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
1575
+ const quotedPropName = validIdentifier.test(propName) ? propName : `"${propName}"`;
1576
+ let propertyDef = `${quotedPropName}: ${zodSchema}`;
1577
+ if (!isRequired) {
1578
+ propertyDef += ".optional()";
1579
+ }
1580
+ properties.push(propertyDef);
1581
+ }
1582
+ }
1583
+ if (properties.length === 0) {
1584
+ return "{}";
1585
+ }
1586
+ return `{
1587
+ ${properties.map((p) => ` ${p}`).join(",\n")}
1588
+ }`;
1589
+ }
1392
1590
  };
1393
1591
  // Performance optimization: Lookup table for faster inclusion checks
1394
1592
  _PropertyGenerator.INCLUSION_RULES = {
@@ -1532,6 +1730,59 @@ function formatFilterStatistics(stats) {
1532
1730
  return lines.join("\n");
1533
1731
  }
1534
1732
 
1733
+ // src/utils/ref-resolver.ts
1734
+ function resolveRef2(obj, spec, maxDepth = 10) {
1735
+ var _a, _b, _c, _d;
1736
+ if (!obj || typeof obj !== "object" || maxDepth <= 0) return obj;
1737
+ if (!obj.$ref) return obj;
1738
+ const ref = obj.$ref;
1739
+ let resolved = null;
1740
+ const paramMatch = ref.match(/^#\/components\/parameters\/(.+)$/);
1741
+ const requestBodyMatch = ref.match(/^#\/components\/requestBodies\/(.+)$/);
1742
+ const responseMatch = ref.match(/^#\/components\/responses\/(.+)$/);
1743
+ const schemaMatch = ref.match(/^#\/components\/schemas\/(.+)$/);
1744
+ if (paramMatch && ((_a = spec.components) == null ? void 0 : _a.parameters)) {
1745
+ const name = paramMatch[1];
1746
+ resolved = spec.components.parameters[name];
1747
+ } else if (requestBodyMatch && ((_b = spec.components) == null ? void 0 : _b.requestBodies)) {
1748
+ const name = requestBodyMatch[1];
1749
+ resolved = spec.components.requestBodies[name];
1750
+ } else if (responseMatch && ((_c = spec.components) == null ? void 0 : _c.responses)) {
1751
+ const name = responseMatch[1];
1752
+ resolved = spec.components.responses[name];
1753
+ } else if (schemaMatch && ((_d = spec.components) == null ? void 0 : _d.schemas)) {
1754
+ const name = schemaMatch[1];
1755
+ resolved = spec.components.schemas[name];
1756
+ }
1757
+ if (resolved) {
1758
+ if (resolved.$ref) {
1759
+ return resolveRef2(resolved, spec, maxDepth - 1);
1760
+ }
1761
+ return resolved;
1762
+ }
1763
+ return obj;
1764
+ }
1765
+ function resolveParameterRef(param, spec) {
1766
+ return resolveRef2(param, spec);
1767
+ }
1768
+ function mergeParameters(pathParams, operationParams, spec) {
1769
+ const resolvedPathParams = (pathParams || []).map((p) => resolveParameterRef(p, spec));
1770
+ const resolvedOperationParams = (operationParams || []).map((p) => resolveParameterRef(p, spec));
1771
+ const merged = [...resolvedPathParams];
1772
+ for (const opParam of resolvedOperationParams) {
1773
+ if (!opParam || typeof opParam !== "object") continue;
1774
+ const existingIndex = merged.findIndex(
1775
+ (p) => p && typeof p === "object" && p.name === opParam.name && p.in === opParam.in
1776
+ );
1777
+ if (existingIndex >= 0) {
1778
+ merged[existingIndex] = opParam;
1779
+ } else {
1780
+ merged.push(opParam);
1781
+ }
1782
+ }
1783
+ return merged;
1784
+ }
1785
+
1535
1786
  // src/openapi-generator.ts
1536
1787
  var OpenApiGenerator = class {
1537
1788
  constructor(options) {
@@ -1541,7 +1792,7 @@ var OpenApiGenerator = class {
1541
1792
  this.schemaUsageMap = /* @__PURE__ */ new Map();
1542
1793
  this.needsZodImport = true;
1543
1794
  this.filterStats = createFilterStatistics();
1544
- var _a, _b, _c, _d, _e, _f, _g;
1795
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1545
1796
  if (!options.input) {
1546
1797
  throw new ConfigurationError("Input path is required", { providedOptions: options });
1547
1798
  }
@@ -1552,18 +1803,19 @@ var OpenApiGenerator = class {
1552
1803
  includeDescriptions: (_a = options.includeDescriptions) != null ? _a : true,
1553
1804
  useDescribe: (_b = options.useDescribe) != null ? _b : false,
1554
1805
  defaultNullable: (_c = options.defaultNullable) != null ? _c : false,
1806
+ emptyObjectBehavior: (_d = options.emptyObjectBehavior) != null ? _d : "loose",
1555
1807
  schemaType: options.schemaType || "all",
1556
1808
  prefix: options.prefix,
1557
1809
  suffix: options.suffix,
1558
1810
  stripSchemaPrefix: options.stripSchemaPrefix,
1559
1811
  stripPathPrefix: options.stripPathPrefix,
1560
- showStats: (_d = options.showStats) != null ? _d : true,
1812
+ showStats: (_e = options.showStats) != null ? _e : true,
1561
1813
  request: options.request,
1562
1814
  response: options.response,
1563
1815
  operationFilters: options.operationFilters,
1564
1816
  ignoreHeaders: options.ignoreHeaders,
1565
- cacheSize: (_e = options.cacheSize) != null ? _e : 1e3,
1566
- batchSize: (_f = options.batchSize) != null ? _f : 10,
1817
+ cacheSize: (_f = options.cacheSize) != null ? _f : 1e3,
1818
+ batchSize: (_g = options.batchSize) != null ? _g : 10,
1567
1819
  customDateTimeFormatRegex: options.customDateTimeFormatRegex
1568
1820
  };
1569
1821
  if (this.options.cacheSize) {
@@ -1634,7 +1886,8 @@ var OpenApiGenerator = class {
1634
1886
  mode: this.requestOptions.mode,
1635
1887
  includeDescriptions: this.requestOptions.includeDescriptions,
1636
1888
  useDescribe: this.requestOptions.useDescribe,
1637
- defaultNullable: (_g = this.options.defaultNullable) != null ? _g : false,
1889
+ defaultNullable: (_h = this.options.defaultNullable) != null ? _h : false,
1890
+ emptyObjectBehavior: (_i = this.options.emptyObjectBehavior) != null ? _i : "loose",
1638
1891
  namingOptions: {
1639
1892
  prefix: this.options.prefix,
1640
1893
  suffix: this.options.suffix
@@ -1704,12 +1957,6 @@ var OpenApiGenerator = class {
1704
1957
  * Generate the complete output file
1705
1958
  */
1706
1959
  generate() {
1707
- if (!this.options.output) {
1708
- throw new ConfigurationError(
1709
- "Output path is required when calling generate(). Either provide an 'output' option or use generateString() to get the result as a string.",
1710
- { hasOutput: false }
1711
- );
1712
- }
1713
1960
  const output = this.generateString();
1714
1961
  const normalizedOutput = normalize(this.options.output);
1715
1962
  this.ensureDirectoryExists(normalizedOutput);
@@ -1988,7 +2235,7 @@ var OpenApiGenerator = class {
1988
2235
  * Generate schema for a component
1989
2236
  */
1990
2237
  generateComponentSchema(name, schema) {
1991
- var _a, _b, _c;
2238
+ var _a, _b, _c, _d;
1992
2239
  if (!this.schemaDependencies.has(name)) {
1993
2240
  this.schemaDependencies.set(name, /* @__PURE__ */ new Set());
1994
2241
  }
@@ -2021,6 +2268,7 @@ ${typeCode}`;
2021
2268
  includeDescriptions: resolvedOptions.includeDescriptions,
2022
2269
  useDescribe: resolvedOptions.useDescribe,
2023
2270
  defaultNullable: (_b = this.options.defaultNullable) != null ? _b : false,
2271
+ emptyObjectBehavior: (_c = this.options.emptyObjectBehavior) != null ? _c : "loose",
2024
2272
  namingOptions: {
2025
2273
  prefix: this.options.prefix,
2026
2274
  suffix: this.options.suffix
@@ -2037,7 +2285,7 @@ ${typeCode}`;
2037
2285
  const depMatch = ref.match(/^([a-z][a-zA-Z0-9]*?)Schema$/);
2038
2286
  if (depMatch) {
2039
2287
  const depName = depMatch[1].charAt(0).toUpperCase() + depMatch[1].slice(1);
2040
- (_c = this.schemaDependencies.get(name)) == null ? void 0 : _c.add(depName);
2288
+ (_d = this.schemaDependencies.get(name)) == null ? void 0 : _d.add(depName);
2041
2289
  }
2042
2290
  }
2043
2291
  }
@@ -2061,10 +2309,8 @@ ${typeCode}`;
2061
2309
  if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
2062
2310
  continue;
2063
2311
  }
2064
- if (!operation.parameters || !Array.isArray(operation.parameters)) {
2065
- continue;
2066
- }
2067
- const queryParams = operation.parameters.filter(
2312
+ const allParams = mergeParameters(pathItem.parameters, operation.parameters, this.spec);
2313
+ const queryParams = allParams.filter(
2068
2314
  (param) => param && typeof param === "object" && param.in === "query"
2069
2315
  );
2070
2316
  if (queryParams.length === 0) {
@@ -2200,10 +2446,8 @@ ${propsCode}
2200
2446
  if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
2201
2447
  continue;
2202
2448
  }
2203
- if (!operation.parameters || !Array.isArray(operation.parameters)) {
2204
- continue;
2205
- }
2206
- const headerParams = operation.parameters.filter(
2449
+ const allParams = mergeParameters(pathItem.parameters, operation.parameters, this.spec);
2450
+ const headerParams = allParams.filter(
2207
2451
  (param) => param && typeof param === "object" && param.in === "header" && !this.shouldIgnoreHeader(param.name)
2208
2452
  );
2209
2453
  if (headerParams.length === 0) {
@@ -2293,13 +2537,23 @@ ${propsCode}
2293
2537
  }
2294
2538
  const type = schema.type;
2295
2539
  if (type === "string") {
2540
+ const formatMap = {
2541
+ email: "z.email()",
2542
+ uri: "z.url()",
2543
+ url: "z.url()",
2544
+ uuid: "z.uuid()"
2545
+ };
2546
+ if (schema.format && formatMap[schema.format]) {
2547
+ let zodType2 = formatMap[schema.format];
2548
+ if (schema.minLength !== void 0) zodType2 = `${zodType2}.min(${schema.minLength})`;
2549
+ if (schema.maxLength !== void 0) zodType2 = `${zodType2}.max(${schema.maxLength})`;
2550
+ if (schema.pattern) zodType2 = `${zodType2}.regex(/${schema.pattern}/)`;
2551
+ return zodType2;
2552
+ }
2296
2553
  let zodType = "z.string()";
2297
2554
  if (schema.minLength !== void 0) zodType = `${zodType}.min(${schema.minLength})`;
2298
2555
  if (schema.maxLength !== void 0) zodType = `${zodType}.max(${schema.maxLength})`;
2299
2556
  if (schema.pattern) zodType = `${zodType}.regex(/${schema.pattern}/)`;
2300
- if (schema.format === "email") zodType = `${zodType}.email()`;
2301
- if (schema.format === "uri" || schema.format === "url") zodType = `${zodType}.url()`;
2302
- if (schema.format === "uuid") zodType = `${zodType}.uuid()`;
2303
2557
  return zodType;
2304
2558
  }
2305
2559
  if (type === "number" || type === "integer") {
@@ -2324,11 +2578,6 @@ ${propsCode}
2324
2578
  }
2325
2579
  return "z.unknown()";
2326
2580
  }
2327
- // REMOVED: generateNativeEnum method - no longer needed as we only generate Zod schemas
2328
- // REMOVED: toEnumKey method - was only used by generateNativeEnum
2329
- // REMOVED: addConstraintsToJSDoc method - was only used for native TypeScript types
2330
- // REMOVED: generateNativeTypeDefinition method - was only used for native TypeScript types
2331
- // REMOVED: generateObjectType method - was only used for native TypeScript types
2332
2581
  /**
2333
2582
  * Topological sort for schema dependencies
2334
2583
  * Returns schemas in the order they should be declared