@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.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { O as OpenApiGeneratorOptions } from './types--r0d47sd.mjs';
2
- export { C as CommonSchemaOptions, a as ConfigFile, E as ExecutionMode, b as OpenAPISchema, c as OpenAPISpec, d as OperationFilters, R as RequestOptions, e as ResponseOptions, f as defineConfig } from './types--r0d47sd.mjs';
1
+ import { O as OpenApiGeneratorOptions } from './types-DZ4Bw-D5.mjs';
2
+ export { C as CommonSchemaOptions, a as ConfigFile, E as ExecutionMode, b as OpenAPIParameter, c as OpenAPIRequestBody, d as OpenAPIResponse, e as OpenAPISchema, f as OpenAPISpec, g as OperationFilters, R as RequestOptions, h as ResponseOptions, i as defineConfig } from './types-DZ4Bw-D5.mjs';
3
3
 
4
4
  /**
5
5
  * Custom error classes for better error handling and debugging
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { O as OpenApiGeneratorOptions } from './types--r0d47sd.js';
2
- export { C as CommonSchemaOptions, a as ConfigFile, E as ExecutionMode, b as OpenAPISchema, c as OpenAPISpec, d as OperationFilters, R as RequestOptions, e as ResponseOptions, f as defineConfig } from './types--r0d47sd.js';
1
+ import { O as OpenApiGeneratorOptions } from './types-DZ4Bw-D5.js';
2
+ export { C as CommonSchemaOptions, a as ConfigFile, E as ExecutionMode, b as OpenAPIParameter, c as OpenAPIRequestBody, d as OpenAPIResponse, e as OpenAPISchema, f as OpenAPISpec, g as OperationFilters, R as RequestOptions, h as ResponseOptions, i as defineConfig } from './types-DZ4Bw-D5.js';
3
3
 
4
4
  /**
5
5
  * Custom error classes for better error handling and debugging
package/dist/index.js CHANGED
@@ -113,12 +113,12 @@ function toCamelCase(str, options) {
113
113
  name = words[0].charAt(0).toLowerCase() + words[0].slice(1) + words.slice(1).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
114
114
  }
115
115
  if (options == null ? void 0 : options.prefix) {
116
- const prefix = options.prefix.toLowerCase();
116
+ const prefix = options.prefix.charAt(0).toLowerCase() + options.prefix.slice(1);
117
117
  name = prefix + name.charAt(0).toUpperCase() + name.slice(1);
118
118
  }
119
119
  if (options == null ? void 0 : options.suffix) {
120
- const suffix = options.suffix;
121
- name = name + suffix.charAt(0).toUpperCase() + suffix.slice(1).toLowerCase();
120
+ const suffix = options.suffix.charAt(0).toUpperCase() + options.suffix.slice(1);
121
+ name = name + suffix;
122
122
  }
123
123
  return name;
124
124
  }
@@ -430,12 +430,56 @@ function generateArrayValidation(schema, context) {
430
430
  }
431
431
 
432
432
  // src/validators/composition-validator.ts
433
+ function isDiscriminatorRequired(schemas, discriminator, context) {
434
+ const invalidSchemas = [];
435
+ for (const schema of schemas) {
436
+ const resolved = resolveSchema(schema, context);
437
+ const required = resolved.required || [];
438
+ if (!required.includes(discriminator)) {
439
+ const schemaName = schema.$ref ? schema.$ref.split("/").pop() || "inline" : "inline";
440
+ invalidSchemas.push(schemaName);
441
+ }
442
+ }
443
+ return {
444
+ valid: invalidSchemas.length === 0,
445
+ invalidSchemas
446
+ };
447
+ }
433
448
  function generateUnion(schemas, discriminator, isNullable2, context, options, currentSchema) {
449
+ if (schemas.length === 0) {
450
+ console.warn(
451
+ "[openapi-to-zod] Warning: Empty oneOf/anyOf array encountered. This is likely a malformed OpenAPI spec. Generating z.never() as fallback."
452
+ );
453
+ return wrapNullable(
454
+ 'z.never().describe("Empty oneOf/anyOf in OpenAPI spec - no valid schema defined")',
455
+ isNullable2
456
+ );
457
+ }
458
+ if (schemas.length === 1) {
459
+ let singleSchema = context.generatePropertySchema(schemas[0], currentSchema);
460
+ if ((options == null ? void 0 : options.passthrough) && !singleSchema.includes(".catchall(")) {
461
+ singleSchema = `${singleSchema}.catchall(z.unknown())`;
462
+ }
463
+ return wrapNullable(singleSchema, isNullable2);
464
+ }
434
465
  if (discriminator) {
435
466
  let resolvedSchemas = schemas;
436
467
  if ((options == null ? void 0 : options.discriminatorMapping) && context.resolveDiscriminatorMapping) {
437
468
  resolvedSchemas = context.resolveDiscriminatorMapping(options.discriminatorMapping, schemas);
438
469
  }
470
+ const discriminatorCheck = isDiscriminatorRequired(resolvedSchemas, discriminator, context);
471
+ if (!discriminatorCheck.valid) {
472
+ console.warn(
473
+ `[openapi-to-zod] Warning: Discriminator "${discriminator}" is not required in schemas: ${discriminatorCheck.invalidSchemas.join(", ")}. Falling back to z.union() instead of z.discriminatedUnion().`
474
+ );
475
+ let schemaStrings3 = resolvedSchemas.map((s) => context.generatePropertySchema(s, currentSchema));
476
+ if (options == null ? void 0 : options.passthrough) {
477
+ schemaStrings3 = schemaStrings3.map((s) => s.includes(".catchall(") ? s : `${s}.catchall(z.unknown())`);
478
+ }
479
+ const fallbackDescription = `Discriminator "${discriminator}" is optional in some schemas (${discriminatorCheck.invalidSchemas.join(", ")}), using z.union() instead of z.discriminatedUnion()`;
480
+ const union3 = `z.union([${schemaStrings3.join(", ")}]).describe("${fallbackDescription}")`;
481
+ return wrapNullable(union3, isNullable2);
482
+ }
439
483
  let schemaStrings2 = resolvedSchemas.map((s) => context.generatePropertySchema(s, currentSchema));
440
484
  if (options == null ? void 0 : options.passthrough) {
441
485
  schemaStrings2 = schemaStrings2.map((s) => s.includes(".catchall(") ? s : `${s}.catchall(z.unknown())`);
@@ -450,25 +494,102 @@ function generateUnion(schemas, discriminator, isNullable2, context, options, cu
450
494
  const union = `z.union([${schemaStrings.join(", ")}])`;
451
495
  return wrapNullable(union, isNullable2);
452
496
  }
453
- function generateAllOf(schemas, isNullable2, context, currentSchema) {
497
+ function resolveSchema(schema, context) {
498
+ if (schema.$ref && context.resolveSchemaRef) {
499
+ const resolved = context.resolveSchemaRef(schema.$ref);
500
+ if (resolved) {
501
+ return resolved;
502
+ }
503
+ }
504
+ return schema;
505
+ }
506
+ function collectProperties(schema, context) {
507
+ const resolved = resolveSchema(schema, context);
508
+ const props = /* @__PURE__ */ new Map();
509
+ const sourceName = schema.$ref ? schema.$ref.split("/").pop() || "unknown" : "inline";
510
+ if (resolved.properties) {
511
+ for (const [key, value] of Object.entries(resolved.properties)) {
512
+ props.set(key, { schema: value, source: sourceName });
513
+ }
514
+ }
515
+ if (resolved.allOf) {
516
+ for (const subSchema of resolved.allOf) {
517
+ const subProps = collectProperties(subSchema, context);
518
+ for (const [key, value] of subProps) {
519
+ if (!props.has(key)) {
520
+ props.set(key, value);
521
+ }
522
+ }
523
+ }
524
+ }
525
+ return props;
526
+ }
527
+ function schemasMatch(a, b) {
528
+ return JSON.stringify(a) === JSON.stringify(b);
529
+ }
530
+ function detectConflictingProperties(schemas, context) {
531
+ const conflicts = [];
532
+ const propertyMap = /* @__PURE__ */ new Map();
533
+ for (const schema of schemas) {
534
+ const schemaProps = collectProperties(schema, context);
535
+ for (const [propName, propInfo] of schemaProps) {
536
+ const existing = propertyMap.get(propName);
537
+ if (existing) {
538
+ if (!schemasMatch(existing.schema, propInfo.schema)) {
539
+ conflicts.push(
540
+ `Property "${propName}" has conflicting definitions in ${existing.source} and ${propInfo.source}`
541
+ );
542
+ }
543
+ } else {
544
+ propertyMap.set(propName, propInfo);
545
+ }
546
+ }
547
+ }
548
+ return conflicts;
549
+ }
550
+ function generateAllOf(schemas, isNullable2, context, currentSchema, explicitNullableFalse = false) {
454
551
  if (schemas.length === 1) {
455
- const singleSchema = context.generatePropertySchema(schemas[0], currentSchema, false);
552
+ const singleSchema = context.generatePropertySchema(schemas[0], currentSchema, false, explicitNullableFalse);
456
553
  return wrapNullable(singleSchema, isNullable2);
457
554
  }
555
+ const conflicts = detectConflictingProperties(schemas, context);
556
+ let conflictDescription = "";
557
+ if (conflicts.length > 0) {
558
+ for (const conflict of conflicts) {
559
+ console.warn(`[openapi-to-zod] Warning: allOf composition conflict - ${conflict}`);
560
+ }
561
+ conflictDescription = `allOf property conflicts detected: ${conflicts.join("; ")}`;
562
+ }
458
563
  const allObjects = schemas.every((s) => s.type === "object" || s.properties || s.$ref || s.allOf);
459
- const schemaStrings = schemas.map((s) => context.generatePropertySchema(s, currentSchema, false));
564
+ let result;
460
565
  if (allObjects) {
461
- let merged2 = schemaStrings[0];
566
+ let merged = context.generatePropertySchema(schemas[0], currentSchema, false);
567
+ for (let i = 1; i < schemas.length; i++) {
568
+ const schema = schemas[i];
569
+ if (schema.$ref) {
570
+ const refSchema = context.generatePropertySchema(schema, currentSchema, false);
571
+ merged = `${merged}.extend(${refSchema}.shape)`;
572
+ } else if (context.generateInlineObjectShape && (schema.properties || schema.type === "object")) {
573
+ const inlineShape = context.generateInlineObjectShape(schema, currentSchema);
574
+ merged = `${merged}.extend(${inlineShape})`;
575
+ } else {
576
+ const schemaString = context.generatePropertySchema(schema, currentSchema, false);
577
+ merged = `${merged}.extend(${schemaString}.shape)`;
578
+ }
579
+ }
580
+ result = merged;
581
+ } else {
582
+ const schemaStrings = schemas.map((s) => context.generatePropertySchema(s, currentSchema, false));
583
+ let merged = schemaStrings[0];
462
584
  for (let i = 1; i < schemaStrings.length; i++) {
463
- merged2 = `${merged2}.merge(${schemaStrings[i]})`;
585
+ merged = `${merged}.and(${schemaStrings[i]})`;
464
586
  }
465
- return wrapNullable(merged2, isNullable2);
587
+ result = merged;
466
588
  }
467
- let merged = schemaStrings[0];
468
- for (let i = 1; i < schemaStrings.length; i++) {
469
- merged = `${merged}.and(${schemaStrings[i]})`;
589
+ if (conflictDescription) {
590
+ result = `${result}.describe("${conflictDescription}")`;
470
591
  }
471
- return wrapNullable(merged, isNullable2);
592
+ return wrapNullable(result, isNullable2);
472
593
  }
473
594
 
474
595
  // src/validators/number-validator.ts
@@ -1144,6 +1265,15 @@ var _PropertyGenerator = class _PropertyGenerator {
1144
1265
  }
1145
1266
  return mappedSchemas;
1146
1267
  }
1268
+ /**
1269
+ * Resolve a $ref string to the actual schema
1270
+ */
1271
+ resolveSchemaRef(ref) {
1272
+ var _a, _b;
1273
+ const schemaName = ref.split("/").pop();
1274
+ if (!schemaName) return void 0;
1275
+ return (_b = (_a = this.context.spec.components) == null ? void 0 : _a.schemas) == null ? void 0 : _b[schemaName];
1276
+ }
1147
1277
  /**
1148
1278
  * Resolve a schema name through any aliases to get the actual schema name
1149
1279
  * If the schema is an alias (allOf with single $ref), return the target name
@@ -1221,7 +1351,7 @@ var _PropertyGenerator = class _PropertyGenerator {
1221
1351
  let schemaWithCatchall = baseSchema;
1222
1352
  if (baseSchema.includes(".union([") || baseSchema.includes(".discriminatedUnion(")) {
1223
1353
  schemaWithCatchall = baseSchema;
1224
- } else if (baseSchema.includes(".merge(")) {
1354
+ } else if (baseSchema.includes(".extend(")) {
1225
1355
  schemaWithCatchall = `${baseSchema}.catchall(z.unknown())`;
1226
1356
  }
1227
1357
  if (schema.unevaluatedProperties === false) {
@@ -1234,12 +1364,21 @@ var _PropertyGenerator = class _PropertyGenerator {
1234
1364
  }
1235
1365
  /**
1236
1366
  * Generate Zod schema for a property
1367
+ * @param schema - The OpenAPI schema to generate
1368
+ * @param currentSchema - The name of the current schema being processed (for circular ref detection)
1369
+ * @param isTopLevel - Whether this is a top-level schema definition
1370
+ * @param suppressDefaultNullable - When true, don't apply defaultNullable (used when outer schema has explicit nullable: false)
1237
1371
  */
1238
- generatePropertySchema(schema, currentSchema, isTopLevel = false) {
1372
+ generatePropertySchema(schema, currentSchema, isTopLevel = false, suppressDefaultNullable = false) {
1239
1373
  var _a, _b, _c, _d, _e;
1240
1374
  const isCacheable = !schema.$ref && !schema.allOf && !schema.oneOf && !schema.anyOf && !currentSchema;
1241
1375
  if (isCacheable) {
1242
- const cacheKey = JSON.stringify({ schema, type: this.context.schemaType, mode: this.context.mode });
1376
+ const cacheKey = JSON.stringify({
1377
+ schema,
1378
+ type: this.context.schemaType,
1379
+ mode: this.context.mode,
1380
+ suppressDefaultNullable
1381
+ });
1243
1382
  const cached = this.schemaCache.get(cacheKey);
1244
1383
  if (cached) {
1245
1384
  return cached;
@@ -1248,7 +1387,10 @@ var _PropertyGenerator = class _PropertyGenerator {
1248
1387
  if ((this.context.schemaType === "request" || this.context.schemaType === "response") && schema.properties) {
1249
1388
  schema = this.filterNestedProperties(schema);
1250
1389
  }
1251
- const effectiveDefaultNullable = isTopLevel ? false : this.context.defaultNullable;
1390
+ const isEnum = !!schema.enum;
1391
+ const isConst = schema.const !== void 0;
1392
+ const shouldApplyDefaultNullable = !isTopLevel && !isEnum && !isConst && !suppressDefaultNullable;
1393
+ const effectiveDefaultNullable = shouldApplyDefaultNullable ? this.context.defaultNullable : false;
1252
1394
  const nullable = isNullable(schema, effectiveDefaultNullable);
1253
1395
  if (hasMultipleTypes(schema)) {
1254
1396
  const union = this.generateMultiTypeUnion(schema, currentSchema);
@@ -1298,11 +1440,17 @@ var _PropertyGenerator = class _PropertyGenerator {
1298
1440
  return wrapNullable(zodUnion, nullable);
1299
1441
  }
1300
1442
  if (schema.allOf) {
1443
+ const explicitNullableFalse = schema.nullable === false;
1301
1444
  let composition = generateAllOf(
1302
1445
  schema.allOf,
1303
1446
  nullable,
1304
- { generatePropertySchema: this.generatePropertySchema.bind(this) },
1305
- currentSchema
1447
+ {
1448
+ generatePropertySchema: this.generatePropertySchema.bind(this),
1449
+ generateInlineObjectShape: this.generateInlineObjectShape.bind(this),
1450
+ resolveSchemaRef: this.resolveSchemaRef.bind(this)
1451
+ },
1452
+ currentSchema,
1453
+ explicitNullableFalse
1306
1454
  );
1307
1455
  if (schema.unevaluatedProperties !== void 0) {
1308
1456
  composition = this.applyUnevaluatedProperties(composition, schema);
@@ -1317,7 +1465,8 @@ var _PropertyGenerator = class _PropertyGenerator {
1317
1465
  nullable,
1318
1466
  {
1319
1467
  generatePropertySchema: this.generatePropertySchema.bind(this),
1320
- resolveDiscriminatorMapping: this.resolveDiscriminatorMapping.bind(this)
1468
+ resolveDiscriminatorMapping: this.resolveDiscriminatorMapping.bind(this),
1469
+ resolveSchemaRef: this.resolveSchemaRef.bind(this)
1321
1470
  },
1322
1471
  {
1323
1472
  passthrough: needsPassthrough,
@@ -1338,7 +1487,8 @@ var _PropertyGenerator = class _PropertyGenerator {
1338
1487
  nullable,
1339
1488
  {
1340
1489
  generatePropertySchema: this.generatePropertySchema.bind(this),
1341
- resolveDiscriminatorMapping: this.resolveDiscriminatorMapping.bind(this)
1490
+ resolveDiscriminatorMapping: this.resolveDiscriminatorMapping.bind(this),
1491
+ resolveSchemaRef: this.resolveSchemaRef.bind(this)
1342
1492
  },
1343
1493
  {
1344
1494
  passthrough: needsPassthrough,
@@ -1401,7 +1551,17 @@ var _PropertyGenerator = class _PropertyGenerator {
1401
1551
  );
1402
1552
  validation = addDescription(validation, schema.description, this.context.useDescribe);
1403
1553
  } else {
1404
- validation = "z.record(z.string(), z.unknown())";
1554
+ switch (this.context.emptyObjectBehavior) {
1555
+ case "strict":
1556
+ validation = "z.strictObject({})";
1557
+ break;
1558
+ case "loose":
1559
+ validation = "z.looseObject({})";
1560
+ break;
1561
+ default:
1562
+ validation = "z.record(z.string(), z.unknown())";
1563
+ break;
1564
+ }
1405
1565
  validation = addDescription(validation, schema.description, this.context.useDescribe);
1406
1566
  }
1407
1567
  break;
@@ -1416,6 +1576,44 @@ var _PropertyGenerator = class _PropertyGenerator {
1416
1576
  }
1417
1577
  return result;
1418
1578
  }
1579
+ /**
1580
+ * Generate inline object shape for use with .extend()
1581
+ * Returns just the shape object literal: { prop1: z.string(), prop2: z.number() }
1582
+ *
1583
+ * This method is specifically for allOf compositions where we need to pass
1584
+ * the shape directly to .extend() instead of using z.object({...}).shape.
1585
+ * This avoids the .nullable().shape bug when inline objects have nullable: true.
1586
+ *
1587
+ * According to Zod docs (https://zod.dev/api?id=extend):
1588
+ * - .extend() accepts an object of shape definitions
1589
+ * - e.g., baseSchema.extend({ prop: z.string() })
1590
+ */
1591
+ generateInlineObjectShape(schema, currentSchema) {
1592
+ const required = new Set(schema.required || []);
1593
+ const properties = [];
1594
+ if (schema.properties) {
1595
+ for (const [propName, propSchema] of Object.entries(schema.properties)) {
1596
+ if (!this.shouldIncludeProperty(propSchema)) {
1597
+ continue;
1598
+ }
1599
+ const isRequired = required.has(propName);
1600
+ const zodSchema = this.generatePropertySchema(propSchema, currentSchema);
1601
+ const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
1602
+ const quotedPropName = validIdentifier.test(propName) ? propName : `"${propName}"`;
1603
+ let propertyDef = `${quotedPropName}: ${zodSchema}`;
1604
+ if (!isRequired) {
1605
+ propertyDef += ".optional()";
1606
+ }
1607
+ properties.push(propertyDef);
1608
+ }
1609
+ }
1610
+ if (properties.length === 0) {
1611
+ return "{}";
1612
+ }
1613
+ return `{
1614
+ ${properties.map((p) => ` ${p}`).join(",\n")}
1615
+ }`;
1616
+ }
1419
1617
  };
1420
1618
  // Performance optimization: Lookup table for faster inclusion checks
1421
1619
  _PropertyGenerator.INCLUSION_RULES = {
@@ -1559,6 +1757,59 @@ function formatFilterStatistics(stats) {
1559
1757
  return lines.join("\n");
1560
1758
  }
1561
1759
 
1760
+ // src/utils/ref-resolver.ts
1761
+ function resolveRef2(obj, spec, maxDepth = 10) {
1762
+ var _a, _b, _c, _d;
1763
+ if (!obj || typeof obj !== "object" || maxDepth <= 0) return obj;
1764
+ if (!obj.$ref) return obj;
1765
+ const ref = obj.$ref;
1766
+ let resolved = null;
1767
+ const paramMatch = ref.match(/^#\/components\/parameters\/(.+)$/);
1768
+ const requestBodyMatch = ref.match(/^#\/components\/requestBodies\/(.+)$/);
1769
+ const responseMatch = ref.match(/^#\/components\/responses\/(.+)$/);
1770
+ const schemaMatch = ref.match(/^#\/components\/schemas\/(.+)$/);
1771
+ if (paramMatch && ((_a = spec.components) == null ? void 0 : _a.parameters)) {
1772
+ const name = paramMatch[1];
1773
+ resolved = spec.components.parameters[name];
1774
+ } else if (requestBodyMatch && ((_b = spec.components) == null ? void 0 : _b.requestBodies)) {
1775
+ const name = requestBodyMatch[1];
1776
+ resolved = spec.components.requestBodies[name];
1777
+ } else if (responseMatch && ((_c = spec.components) == null ? void 0 : _c.responses)) {
1778
+ const name = responseMatch[1];
1779
+ resolved = spec.components.responses[name];
1780
+ } else if (schemaMatch && ((_d = spec.components) == null ? void 0 : _d.schemas)) {
1781
+ const name = schemaMatch[1];
1782
+ resolved = spec.components.schemas[name];
1783
+ }
1784
+ if (resolved) {
1785
+ if (resolved.$ref) {
1786
+ return resolveRef2(resolved, spec, maxDepth - 1);
1787
+ }
1788
+ return resolved;
1789
+ }
1790
+ return obj;
1791
+ }
1792
+ function resolveParameterRef(param, spec) {
1793
+ return resolveRef2(param, spec);
1794
+ }
1795
+ function mergeParameters(pathParams, operationParams, spec) {
1796
+ const resolvedPathParams = (pathParams || []).map((p) => resolveParameterRef(p, spec));
1797
+ const resolvedOperationParams = (operationParams || []).map((p) => resolveParameterRef(p, spec));
1798
+ const merged = [...resolvedPathParams];
1799
+ for (const opParam of resolvedOperationParams) {
1800
+ if (!opParam || typeof opParam !== "object") continue;
1801
+ const existingIndex = merged.findIndex(
1802
+ (p) => p && typeof p === "object" && p.name === opParam.name && p.in === opParam.in
1803
+ );
1804
+ if (existingIndex >= 0) {
1805
+ merged[existingIndex] = opParam;
1806
+ } else {
1807
+ merged.push(opParam);
1808
+ }
1809
+ }
1810
+ return merged;
1811
+ }
1812
+
1562
1813
  // src/openapi-generator.ts
1563
1814
  var OpenApiGenerator = class {
1564
1815
  constructor(options) {
@@ -1568,7 +1819,7 @@ var OpenApiGenerator = class {
1568
1819
  this.schemaUsageMap = /* @__PURE__ */ new Map();
1569
1820
  this.needsZodImport = true;
1570
1821
  this.filterStats = createFilterStatistics();
1571
- var _a, _b, _c, _d, _e, _f, _g;
1822
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1572
1823
  if (!options.input) {
1573
1824
  throw new ConfigurationError("Input path is required", { providedOptions: options });
1574
1825
  }
@@ -1579,18 +1830,19 @@ var OpenApiGenerator = class {
1579
1830
  includeDescriptions: (_a = options.includeDescriptions) != null ? _a : true,
1580
1831
  useDescribe: (_b = options.useDescribe) != null ? _b : false,
1581
1832
  defaultNullable: (_c = options.defaultNullable) != null ? _c : false,
1833
+ emptyObjectBehavior: (_d = options.emptyObjectBehavior) != null ? _d : "loose",
1582
1834
  schemaType: options.schemaType || "all",
1583
1835
  prefix: options.prefix,
1584
1836
  suffix: options.suffix,
1585
1837
  stripSchemaPrefix: options.stripSchemaPrefix,
1586
1838
  stripPathPrefix: options.stripPathPrefix,
1587
- showStats: (_d = options.showStats) != null ? _d : true,
1839
+ showStats: (_e = options.showStats) != null ? _e : true,
1588
1840
  request: options.request,
1589
1841
  response: options.response,
1590
1842
  operationFilters: options.operationFilters,
1591
1843
  ignoreHeaders: options.ignoreHeaders,
1592
- cacheSize: (_e = options.cacheSize) != null ? _e : 1e3,
1593
- batchSize: (_f = options.batchSize) != null ? _f : 10,
1844
+ cacheSize: (_f = options.cacheSize) != null ? _f : 1e3,
1845
+ batchSize: (_g = options.batchSize) != null ? _g : 10,
1594
1846
  customDateTimeFormatRegex: options.customDateTimeFormatRegex
1595
1847
  };
1596
1848
  if (this.options.cacheSize) {
@@ -1661,7 +1913,8 @@ var OpenApiGenerator = class {
1661
1913
  mode: this.requestOptions.mode,
1662
1914
  includeDescriptions: this.requestOptions.includeDescriptions,
1663
1915
  useDescribe: this.requestOptions.useDescribe,
1664
- defaultNullable: (_g = this.options.defaultNullable) != null ? _g : false,
1916
+ defaultNullable: (_h = this.options.defaultNullable) != null ? _h : false,
1917
+ emptyObjectBehavior: (_i = this.options.emptyObjectBehavior) != null ? _i : "loose",
1665
1918
  namingOptions: {
1666
1919
  prefix: this.options.prefix,
1667
1920
  suffix: this.options.suffix
@@ -1731,12 +1984,6 @@ var OpenApiGenerator = class {
1731
1984
  * Generate the complete output file
1732
1985
  */
1733
1986
  generate() {
1734
- if (!this.options.output) {
1735
- throw new ConfigurationError(
1736
- "Output path is required when calling generate(). Either provide an 'output' option or use generateString() to get the result as a string.",
1737
- { hasOutput: false }
1738
- );
1739
- }
1740
1987
  const output = this.generateString();
1741
1988
  const normalizedOutput = (0, import_node_path.normalize)(this.options.output);
1742
1989
  this.ensureDirectoryExists(normalizedOutput);
@@ -2015,7 +2262,7 @@ var OpenApiGenerator = class {
2015
2262
  * Generate schema for a component
2016
2263
  */
2017
2264
  generateComponentSchema(name, schema) {
2018
- var _a, _b, _c;
2265
+ var _a, _b, _c, _d;
2019
2266
  if (!this.schemaDependencies.has(name)) {
2020
2267
  this.schemaDependencies.set(name, /* @__PURE__ */ new Set());
2021
2268
  }
@@ -2048,6 +2295,7 @@ ${typeCode}`;
2048
2295
  includeDescriptions: resolvedOptions.includeDescriptions,
2049
2296
  useDescribe: resolvedOptions.useDescribe,
2050
2297
  defaultNullable: (_b = this.options.defaultNullable) != null ? _b : false,
2298
+ emptyObjectBehavior: (_c = this.options.emptyObjectBehavior) != null ? _c : "loose",
2051
2299
  namingOptions: {
2052
2300
  prefix: this.options.prefix,
2053
2301
  suffix: this.options.suffix
@@ -2064,7 +2312,7 @@ ${typeCode}`;
2064
2312
  const depMatch = ref.match(/^([a-z][a-zA-Z0-9]*?)Schema$/);
2065
2313
  if (depMatch) {
2066
2314
  const depName = depMatch[1].charAt(0).toUpperCase() + depMatch[1].slice(1);
2067
- (_c = this.schemaDependencies.get(name)) == null ? void 0 : _c.add(depName);
2315
+ (_d = this.schemaDependencies.get(name)) == null ? void 0 : _d.add(depName);
2068
2316
  }
2069
2317
  }
2070
2318
  }
@@ -2088,10 +2336,8 @@ ${typeCode}`;
2088
2336
  if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
2089
2337
  continue;
2090
2338
  }
2091
- if (!operation.parameters || !Array.isArray(operation.parameters)) {
2092
- continue;
2093
- }
2094
- const queryParams = operation.parameters.filter(
2339
+ const allParams = mergeParameters(pathItem.parameters, operation.parameters, this.spec);
2340
+ const queryParams = allParams.filter(
2095
2341
  (param) => param && typeof param === "object" && param.in === "query"
2096
2342
  );
2097
2343
  if (queryParams.length === 0) {
@@ -2227,10 +2473,8 @@ ${propsCode}
2227
2473
  if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
2228
2474
  continue;
2229
2475
  }
2230
- if (!operation.parameters || !Array.isArray(operation.parameters)) {
2231
- continue;
2232
- }
2233
- const headerParams = operation.parameters.filter(
2476
+ const allParams = mergeParameters(pathItem.parameters, operation.parameters, this.spec);
2477
+ const headerParams = allParams.filter(
2234
2478
  (param) => param && typeof param === "object" && param.in === "header" && !this.shouldIgnoreHeader(param.name)
2235
2479
  );
2236
2480
  if (headerParams.length === 0) {
@@ -2320,13 +2564,23 @@ ${propsCode}
2320
2564
  }
2321
2565
  const type = schema.type;
2322
2566
  if (type === "string") {
2567
+ const formatMap = {
2568
+ email: "z.email()",
2569
+ uri: "z.url()",
2570
+ url: "z.url()",
2571
+ uuid: "z.uuid()"
2572
+ };
2573
+ if (schema.format && formatMap[schema.format]) {
2574
+ let zodType2 = formatMap[schema.format];
2575
+ if (schema.minLength !== void 0) zodType2 = `${zodType2}.min(${schema.minLength})`;
2576
+ if (schema.maxLength !== void 0) zodType2 = `${zodType2}.max(${schema.maxLength})`;
2577
+ if (schema.pattern) zodType2 = `${zodType2}.regex(/${schema.pattern}/)`;
2578
+ return zodType2;
2579
+ }
2323
2580
  let zodType = "z.string()";
2324
2581
  if (schema.minLength !== void 0) zodType = `${zodType}.min(${schema.minLength})`;
2325
2582
  if (schema.maxLength !== void 0) zodType = `${zodType}.max(${schema.maxLength})`;
2326
2583
  if (schema.pattern) zodType = `${zodType}.regex(/${schema.pattern}/)`;
2327
- if (schema.format === "email") zodType = `${zodType}.email()`;
2328
- if (schema.format === "uri" || schema.format === "url") zodType = `${zodType}.url()`;
2329
- if (schema.format === "uuid") zodType = `${zodType}.uuid()`;
2330
2584
  return zodType;
2331
2585
  }
2332
2586
  if (type === "number" || type === "integer") {
@@ -2351,11 +2605,6 @@ ${propsCode}
2351
2605
  }
2352
2606
  return "z.unknown()";
2353
2607
  }
2354
- // REMOVED: generateNativeEnum method - no longer needed as we only generate Zod schemas
2355
- // REMOVED: toEnumKey method - was only used by generateNativeEnum
2356
- // REMOVED: addConstraintsToJSDoc method - was only used for native TypeScript types
2357
- // REMOVED: generateNativeTypeDefinition method - was only used for native TypeScript types
2358
- // REMOVED: generateObjectType method - was only used for native TypeScript types
2359
2608
  /**
2360
2609
  * Topological sort for schema dependencies
2361
2610
  * Returns schemas in the order they should be declared