@cerios/openapi-to-zod 1.1.1 → 1.3.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/cli.js CHANGED
@@ -5233,14 +5233,17 @@ function escapeJSDoc(str) {
5233
5233
  function wrapNullable(validation, isNullable2) {
5234
5234
  return isNullable2 ? `${validation}.nullable()` : validation;
5235
5235
  }
5236
- function isNullable(schema) {
5236
+ function isNullable(schema, defaultNullable = false) {
5237
5237
  if (schema.nullable === true) {
5238
5238
  return true;
5239
5239
  }
5240
+ if (schema.nullable === false) {
5241
+ return false;
5242
+ }
5240
5243
  if (Array.isArray(schema.type)) {
5241
5244
  return schema.type.includes("null");
5242
5245
  }
5243
- return false;
5246
+ return defaultNullable;
5244
5247
  }
5245
5248
  function getPrimaryType(schema) {
5246
5249
  if (Array.isArray(schema.type)) {
@@ -5411,6 +5414,22 @@ function stripPrefix(input, pattern, ensureLeadingChar) {
5411
5414
  }
5412
5415
  return input;
5413
5416
  }
5417
+ function stripPathPrefix(path, pattern) {
5418
+ if (!pattern) {
5419
+ return path;
5420
+ }
5421
+ if (!isGlobPattern(pattern)) {
5422
+ let normalizedPattern = pattern.trim();
5423
+ if (!normalizedPattern.startsWith("/")) {
5424
+ normalizedPattern = `/${normalizedPattern}`;
5425
+ }
5426
+ if (normalizedPattern.endsWith("/") && normalizedPattern !== "/") {
5427
+ normalizedPattern = normalizedPattern.slice(0, -1);
5428
+ }
5429
+ return stripPrefix(path, normalizedPattern, "/");
5430
+ }
5431
+ return stripPrefix(path, pattern, "/");
5432
+ }
5414
5433
 
5415
5434
  // src/validators/array-validator.ts
5416
5435
  init_cjs_shims();
@@ -5461,12 +5480,56 @@ function generateArrayValidation(schema, context) {
5461
5480
 
5462
5481
  // src/validators/composition-validator.ts
5463
5482
  init_cjs_shims();
5483
+ function isDiscriminatorRequired(schemas, discriminator, context) {
5484
+ const invalidSchemas = [];
5485
+ for (const schema of schemas) {
5486
+ const resolved = resolveSchema(schema, context);
5487
+ const required = resolved.required || [];
5488
+ if (!required.includes(discriminator)) {
5489
+ const schemaName = schema.$ref ? schema.$ref.split("/").pop() || "inline" : "inline";
5490
+ invalidSchemas.push(schemaName);
5491
+ }
5492
+ }
5493
+ return {
5494
+ valid: invalidSchemas.length === 0,
5495
+ invalidSchemas
5496
+ };
5497
+ }
5464
5498
  function generateUnion(schemas, discriminator, isNullable2, context, options, currentSchema) {
5499
+ if (schemas.length === 0) {
5500
+ console.warn(
5501
+ "[openapi-to-zod] Warning: Empty oneOf/anyOf array encountered. This is likely a malformed OpenAPI spec. Generating z.never() as fallback."
5502
+ );
5503
+ return wrapNullable(
5504
+ 'z.never().describe("Empty oneOf/anyOf in OpenAPI spec - no valid schema defined")',
5505
+ isNullable2
5506
+ );
5507
+ }
5508
+ if (schemas.length === 1) {
5509
+ let singleSchema = context.generatePropertySchema(schemas[0], currentSchema);
5510
+ if ((options == null ? void 0 : options.passthrough) && !singleSchema.includes(".catchall(")) {
5511
+ singleSchema = `${singleSchema}.catchall(z.unknown())`;
5512
+ }
5513
+ return wrapNullable(singleSchema, isNullable2);
5514
+ }
5465
5515
  if (discriminator) {
5466
5516
  let resolvedSchemas = schemas;
5467
5517
  if ((options == null ? void 0 : options.discriminatorMapping) && context.resolveDiscriminatorMapping) {
5468
5518
  resolvedSchemas = context.resolveDiscriminatorMapping(options.discriminatorMapping, schemas);
5469
5519
  }
5520
+ const discriminatorCheck = isDiscriminatorRequired(resolvedSchemas, discriminator, context);
5521
+ if (!discriminatorCheck.valid) {
5522
+ console.warn(
5523
+ `[openapi-to-zod] Warning: Discriminator "${discriminator}" is not required in schemas: ${discriminatorCheck.invalidSchemas.join(", ")}. Falling back to z.union() instead of z.discriminatedUnion().`
5524
+ );
5525
+ let schemaStrings3 = resolvedSchemas.map((s) => context.generatePropertySchema(s, currentSchema));
5526
+ if (options == null ? void 0 : options.passthrough) {
5527
+ schemaStrings3 = schemaStrings3.map((s) => s.includes(".catchall(") ? s : `${s}.catchall(z.unknown())`);
5528
+ }
5529
+ const fallbackDescription = `Discriminator "${discriminator}" is optional in some schemas (${discriminatorCheck.invalidSchemas.join(", ")}), using z.union() instead of z.discriminatedUnion()`;
5530
+ const union3 = `z.union([${schemaStrings3.join(", ")}]).describe("${fallbackDescription}")`;
5531
+ return wrapNullable(union3, isNullable2);
5532
+ }
5470
5533
  let schemaStrings2 = resolvedSchemas.map((s) => context.generatePropertySchema(s, currentSchema));
5471
5534
  if (options == null ? void 0 : options.passthrough) {
5472
5535
  schemaStrings2 = schemaStrings2.map((s) => s.includes(".catchall(") ? s : `${s}.catchall(z.unknown())`);
@@ -5481,25 +5544,102 @@ function generateUnion(schemas, discriminator, isNullable2, context, options, cu
5481
5544
  const union = `z.union([${schemaStrings.join(", ")}])`;
5482
5545
  return wrapNullable(union, isNullable2);
5483
5546
  }
5547
+ function resolveSchema(schema, context) {
5548
+ if (schema.$ref && context.resolveSchemaRef) {
5549
+ const resolved = context.resolveSchemaRef(schema.$ref);
5550
+ if (resolved) {
5551
+ return resolved;
5552
+ }
5553
+ }
5554
+ return schema;
5555
+ }
5556
+ function collectProperties(schema, context) {
5557
+ const resolved = resolveSchema(schema, context);
5558
+ const props = /* @__PURE__ */ new Map();
5559
+ const sourceName = schema.$ref ? schema.$ref.split("/").pop() || "unknown" : "inline";
5560
+ if (resolved.properties) {
5561
+ for (const [key, value] of Object.entries(resolved.properties)) {
5562
+ props.set(key, { schema: value, source: sourceName });
5563
+ }
5564
+ }
5565
+ if (resolved.allOf) {
5566
+ for (const subSchema of resolved.allOf) {
5567
+ const subProps = collectProperties(subSchema, context);
5568
+ for (const [key, value] of subProps) {
5569
+ if (!props.has(key)) {
5570
+ props.set(key, value);
5571
+ }
5572
+ }
5573
+ }
5574
+ }
5575
+ return props;
5576
+ }
5577
+ function schemasMatch(a, b) {
5578
+ return JSON.stringify(a) === JSON.stringify(b);
5579
+ }
5580
+ function detectConflictingProperties(schemas, context) {
5581
+ const conflicts = [];
5582
+ const propertyMap = /* @__PURE__ */ new Map();
5583
+ for (const schema of schemas) {
5584
+ const schemaProps = collectProperties(schema, context);
5585
+ for (const [propName, propInfo] of schemaProps) {
5586
+ const existing = propertyMap.get(propName);
5587
+ if (existing) {
5588
+ if (!schemasMatch(existing.schema, propInfo.schema)) {
5589
+ conflicts.push(
5590
+ `Property "${propName}" has conflicting definitions in ${existing.source} and ${propInfo.source}`
5591
+ );
5592
+ }
5593
+ } else {
5594
+ propertyMap.set(propName, propInfo);
5595
+ }
5596
+ }
5597
+ }
5598
+ return conflicts;
5599
+ }
5484
5600
  function generateAllOf(schemas, isNullable2, context, currentSchema) {
5485
5601
  if (schemas.length === 1) {
5486
5602
  const singleSchema = context.generatePropertySchema(schemas[0], currentSchema, false);
5487
5603
  return wrapNullable(singleSchema, isNullable2);
5488
5604
  }
5605
+ const conflicts = detectConflictingProperties(schemas, context);
5606
+ let conflictDescription = "";
5607
+ if (conflicts.length > 0) {
5608
+ for (const conflict of conflicts) {
5609
+ console.warn(`[openapi-to-zod] Warning: allOf composition conflict - ${conflict}`);
5610
+ }
5611
+ conflictDescription = `allOf property conflicts detected: ${conflicts.join("; ")}`;
5612
+ }
5489
5613
  const allObjects = schemas.every((s) => s.type === "object" || s.properties || s.$ref || s.allOf);
5490
- const schemaStrings = schemas.map((s) => context.generatePropertySchema(s, currentSchema, false));
5614
+ let result;
5491
5615
  if (allObjects) {
5492
- let merged2 = schemaStrings[0];
5616
+ let merged = context.generatePropertySchema(schemas[0], currentSchema, false);
5617
+ for (let i = 1; i < schemas.length; i++) {
5618
+ const schema = schemas[i];
5619
+ if (schema.$ref) {
5620
+ const refSchema = context.generatePropertySchema(schema, currentSchema, false);
5621
+ merged = `${merged}.extend(${refSchema}.shape)`;
5622
+ } else if (context.generateInlineObjectShape && (schema.properties || schema.type === "object")) {
5623
+ const inlineShape = context.generateInlineObjectShape(schema, currentSchema);
5624
+ merged = `${merged}.extend(${inlineShape})`;
5625
+ } else {
5626
+ const schemaString = context.generatePropertySchema(schema, currentSchema, false);
5627
+ merged = `${merged}.extend(${schemaString}.shape)`;
5628
+ }
5629
+ }
5630
+ result = merged;
5631
+ } else {
5632
+ const schemaStrings = schemas.map((s) => context.generatePropertySchema(s, currentSchema, false));
5633
+ let merged = schemaStrings[0];
5493
5634
  for (let i = 1; i < schemaStrings.length; i++) {
5494
- merged2 = `${merged2}.merge(${schemaStrings[i]})`;
5635
+ merged = `${merged}.and(${schemaStrings[i]})`;
5495
5636
  }
5496
- return wrapNullable(merged2, isNullable2);
5637
+ result = merged;
5497
5638
  }
5498
- let merged = schemaStrings[0];
5499
- for (let i = 1; i < schemaStrings.length; i++) {
5500
- merged = `${merged}.and(${schemaStrings[i]})`;
5639
+ if (conflictDescription) {
5640
+ result = `${result}.describe("${conflictDescription}")`;
5501
5641
  }
5502
- return wrapNullable(merged, isNullable2);
5642
+ return wrapNullable(result, isNullable2);
5503
5643
  }
5504
5644
 
5505
5645
  // src/validators/number-validator.ts
@@ -5944,7 +6084,7 @@ function configurePatternCache(size) {
5944
6084
  PATTERN_CACHE = new LRUCache(size);
5945
6085
  }
5946
6086
  }
5947
- var FORMAT_MAP = {
6087
+ var DEFAULT_FORMAT_MAP = {
5948
6088
  uuid: "z.uuid()",
5949
6089
  email: "z.email()",
5950
6090
  uri: "z.url()",
@@ -5954,7 +6094,6 @@ var FORMAT_MAP = {
5954
6094
  byte: "z.base64()",
5955
6095
  binary: "z.string()",
5956
6096
  date: "z.iso.date()",
5957
- "date-time": "z.iso.datetime()",
5958
6097
  time: "z.iso.time()",
5959
6098
  duration: 'z.string().refine((val) => /^P(?:(?:\\d+Y)?(?:\\d+M)?(?:\\d+D)?(?:T(?:\\d+H)?(?:\\d+M)?(?:\\d+(?:\\.\\d+)?S)?)?|\\d+W)$/.test(val) && !/^PT?$/.test(val), { message: "Must be a valid ISO 8601 duration" })',
5960
6099
  ipv4: "z.ipv4()",
@@ -5973,6 +6112,30 @@ var FORMAT_MAP = {
5973
6112
  "json-pointer": 'z.string().refine((val) => val === "" || /^(\\/([^~/]|~0|~1)+)+$/.test(val), { message: "Must be a valid JSON Pointer (RFC 6901)" })',
5974
6113
  "relative-json-pointer": 'z.string().refine((val) => /^(0|[1-9]\\d*)(#|(\\/([^~/]|~0|~1)+)*)$/.test(val), { message: "Must be a valid relative JSON Pointer" })'
5975
6114
  };
6115
+ var FORMAT_MAP = {
6116
+ ...DEFAULT_FORMAT_MAP,
6117
+ "date-time": "z.iso.datetime()"
6118
+ };
6119
+ function configureDateTimeFormat(pattern) {
6120
+ if (!pattern) {
6121
+ FORMAT_MAP["date-time"] = "z.iso.datetime()";
6122
+ return;
6123
+ }
6124
+ const patternStr = pattern instanceof RegExp ? pattern.source : pattern;
6125
+ if (patternStr === "") {
6126
+ FORMAT_MAP["date-time"] = "z.iso.datetime()";
6127
+ return;
6128
+ }
6129
+ try {
6130
+ new RegExp(patternStr);
6131
+ } catch (error) {
6132
+ throw new Error(
6133
+ `Invalid regular expression pattern for customDateTimeFormatRegex: ${patternStr}. ${error instanceof Error ? error.message : "Pattern is malformed"}`
6134
+ );
6135
+ }
6136
+ const escapedPattern = escapePattern(patternStr);
6137
+ FORMAT_MAP["date-time"] = `z.string().regex(/${escapedPattern}/)`;
6138
+ }
5976
6139
  function generateStringValidation(schema, useDescribe) {
5977
6140
  let validation = FORMAT_MAP[schema.format || ""] || "z.string()";
5978
6141
  if (schema.minLength !== void 0) {
@@ -6158,6 +6321,15 @@ var _PropertyGenerator = class _PropertyGenerator {
6158
6321
  }
6159
6322
  return mappedSchemas;
6160
6323
  }
6324
+ /**
6325
+ * Resolve a $ref string to the actual schema
6326
+ */
6327
+ resolveSchemaRef(ref) {
6328
+ var _a, _b;
6329
+ const schemaName = ref.split("/").pop();
6330
+ if (!schemaName) return void 0;
6331
+ return (_b = (_a = this.context.spec.components) == null ? void 0 : _a.schemas) == null ? void 0 : _b[schemaName];
6332
+ }
6161
6333
  /**
6162
6334
  * Resolve a schema name through any aliases to get the actual schema name
6163
6335
  * If the schema is an alias (allOf with single $ref), return the target name
@@ -6235,7 +6407,7 @@ var _PropertyGenerator = class _PropertyGenerator {
6235
6407
  let schemaWithCatchall = baseSchema;
6236
6408
  if (baseSchema.includes(".union([") || baseSchema.includes(".discriminatedUnion(")) {
6237
6409
  schemaWithCatchall = baseSchema;
6238
- } else if (baseSchema.includes(".merge(")) {
6410
+ } else if (baseSchema.includes(".extend(")) {
6239
6411
  schemaWithCatchall = `${baseSchema}.catchall(z.unknown())`;
6240
6412
  }
6241
6413
  if (schema.unevaluatedProperties === false) {
@@ -6262,7 +6434,12 @@ var _PropertyGenerator = class _PropertyGenerator {
6262
6434
  if ((this.context.schemaType === "request" || this.context.schemaType === "response") && schema.properties) {
6263
6435
  schema = this.filterNestedProperties(schema);
6264
6436
  }
6265
- const nullable = isNullable(schema);
6437
+ const isSchemaRef = !!schema.$ref;
6438
+ const isEnum = !!schema.enum;
6439
+ const isConst = schema.const !== void 0;
6440
+ const shouldApplyDefaultNullable = !isTopLevel && !isSchemaRef && !isEnum && !isConst;
6441
+ const effectiveDefaultNullable = shouldApplyDefaultNullable ? this.context.defaultNullable : false;
6442
+ const nullable = isNullable(schema, effectiveDefaultNullable);
6266
6443
  if (hasMultipleTypes(schema)) {
6267
6444
  const union = this.generateMultiTypeUnion(schema, currentSchema);
6268
6445
  return wrapNullable(union, nullable);
@@ -6314,7 +6491,11 @@ var _PropertyGenerator = class _PropertyGenerator {
6314
6491
  let composition = generateAllOf(
6315
6492
  schema.allOf,
6316
6493
  nullable,
6317
- { generatePropertySchema: this.generatePropertySchema.bind(this) },
6494
+ {
6495
+ generatePropertySchema: this.generatePropertySchema.bind(this),
6496
+ generateInlineObjectShape: this.generateInlineObjectShape.bind(this),
6497
+ resolveSchemaRef: this.resolveSchemaRef.bind(this)
6498
+ },
6318
6499
  currentSchema
6319
6500
  );
6320
6501
  if (schema.unevaluatedProperties !== void 0) {
@@ -6330,7 +6511,8 @@ var _PropertyGenerator = class _PropertyGenerator {
6330
6511
  nullable,
6331
6512
  {
6332
6513
  generatePropertySchema: this.generatePropertySchema.bind(this),
6333
- resolveDiscriminatorMapping: this.resolveDiscriminatorMapping.bind(this)
6514
+ resolveDiscriminatorMapping: this.resolveDiscriminatorMapping.bind(this),
6515
+ resolveSchemaRef: this.resolveSchemaRef.bind(this)
6334
6516
  },
6335
6517
  {
6336
6518
  passthrough: needsPassthrough,
@@ -6351,7 +6533,8 @@ var _PropertyGenerator = class _PropertyGenerator {
6351
6533
  nullable,
6352
6534
  {
6353
6535
  generatePropertySchema: this.generatePropertySchema.bind(this),
6354
- resolveDiscriminatorMapping: this.resolveDiscriminatorMapping.bind(this)
6536
+ resolveDiscriminatorMapping: this.resolveDiscriminatorMapping.bind(this),
6537
+ resolveSchemaRef: this.resolveSchemaRef.bind(this)
6355
6538
  },
6356
6539
  {
6357
6540
  passthrough: needsPassthrough,
@@ -6414,7 +6597,17 @@ var _PropertyGenerator = class _PropertyGenerator {
6414
6597
  );
6415
6598
  validation = addDescription(validation, schema.description, this.context.useDescribe);
6416
6599
  } else {
6417
- validation = "z.record(z.string(), z.unknown())";
6600
+ switch (this.context.emptyObjectBehavior) {
6601
+ case "strict":
6602
+ validation = "z.strictObject({})";
6603
+ break;
6604
+ case "loose":
6605
+ validation = "z.looseObject({})";
6606
+ break;
6607
+ default:
6608
+ validation = "z.record(z.string(), z.unknown())";
6609
+ break;
6610
+ }
6418
6611
  validation = addDescription(validation, schema.description, this.context.useDescribe);
6419
6612
  }
6420
6613
  break;
@@ -6429,6 +6622,44 @@ var _PropertyGenerator = class _PropertyGenerator {
6429
6622
  }
6430
6623
  return result;
6431
6624
  }
6625
+ /**
6626
+ * Generate inline object shape for use with .extend()
6627
+ * Returns just the shape object literal: { prop1: z.string(), prop2: z.number() }
6628
+ *
6629
+ * This method is specifically for allOf compositions where we need to pass
6630
+ * the shape directly to .extend() instead of using z.object({...}).shape.
6631
+ * This avoids the .nullable().shape bug when inline objects have nullable: true.
6632
+ *
6633
+ * According to Zod docs (https://zod.dev/api?id=extend):
6634
+ * - .extend() accepts an object of shape definitions
6635
+ * - e.g., baseSchema.extend({ prop: z.string() })
6636
+ */
6637
+ generateInlineObjectShape(schema, currentSchema) {
6638
+ const required = new Set(schema.required || []);
6639
+ const properties = [];
6640
+ if (schema.properties) {
6641
+ for (const [propName, propSchema] of Object.entries(schema.properties)) {
6642
+ if (!this.shouldIncludeProperty(propSchema)) {
6643
+ continue;
6644
+ }
6645
+ const isRequired = required.has(propName);
6646
+ const zodSchema = this.generatePropertySchema(propSchema, currentSchema);
6647
+ const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
6648
+ const quotedPropName = validIdentifier.test(propName) ? propName : `"${propName}"`;
6649
+ let propertyDef = `${quotedPropName}: ${zodSchema}`;
6650
+ if (!isRequired) {
6651
+ propertyDef += ".optional()";
6652
+ }
6653
+ properties.push(propertyDef);
6654
+ }
6655
+ }
6656
+ if (properties.length === 0) {
6657
+ return "{}";
6658
+ }
6659
+ return `{
6660
+ ${properties.map((p) => ` ${p}`).join(",\n")}
6661
+ }`;
6662
+ }
6432
6663
  };
6433
6664
  // Performance optimization: Lookup table for faster inclusion checks
6434
6665
  _PropertyGenerator.INCLUSION_RULES = {
@@ -6573,6 +6804,60 @@ function formatFilterStatistics(stats) {
6573
6804
  return lines.join("\n");
6574
6805
  }
6575
6806
 
6807
+ // src/utils/ref-resolver.ts
6808
+ init_cjs_shims();
6809
+ function resolveRef2(obj, spec, maxDepth = 10) {
6810
+ var _a, _b, _c, _d;
6811
+ if (!obj || typeof obj !== "object" || maxDepth <= 0) return obj;
6812
+ if (!obj.$ref) return obj;
6813
+ const ref = obj.$ref;
6814
+ let resolved = null;
6815
+ const paramMatch = ref.match(/^#\/components\/parameters\/(.+)$/);
6816
+ const requestBodyMatch = ref.match(/^#\/components\/requestBodies\/(.+)$/);
6817
+ const responseMatch = ref.match(/^#\/components\/responses\/(.+)$/);
6818
+ const schemaMatch = ref.match(/^#\/components\/schemas\/(.+)$/);
6819
+ if (paramMatch && ((_a = spec.components) == null ? void 0 : _a.parameters)) {
6820
+ const name = paramMatch[1];
6821
+ resolved = spec.components.parameters[name];
6822
+ } else if (requestBodyMatch && ((_b = spec.components) == null ? void 0 : _b.requestBodies)) {
6823
+ const name = requestBodyMatch[1];
6824
+ resolved = spec.components.requestBodies[name];
6825
+ } else if (responseMatch && ((_c = spec.components) == null ? void 0 : _c.responses)) {
6826
+ const name = responseMatch[1];
6827
+ resolved = spec.components.responses[name];
6828
+ } else if (schemaMatch && ((_d = spec.components) == null ? void 0 : _d.schemas)) {
6829
+ const name = schemaMatch[1];
6830
+ resolved = spec.components.schemas[name];
6831
+ }
6832
+ if (resolved) {
6833
+ if (resolved.$ref) {
6834
+ return resolveRef2(resolved, spec, maxDepth - 1);
6835
+ }
6836
+ return resolved;
6837
+ }
6838
+ return obj;
6839
+ }
6840
+ function resolveParameterRef(param, spec) {
6841
+ return resolveRef2(param, spec);
6842
+ }
6843
+ function mergeParameters(pathParams, operationParams, spec) {
6844
+ const resolvedPathParams = (pathParams || []).map((p) => resolveParameterRef(p, spec));
6845
+ const resolvedOperationParams = (operationParams || []).map((p) => resolveParameterRef(p, spec));
6846
+ const merged = [...resolvedPathParams];
6847
+ for (const opParam of resolvedOperationParams) {
6848
+ if (!opParam || typeof opParam !== "object") continue;
6849
+ const existingIndex = merged.findIndex(
6850
+ (p) => p && typeof p === "object" && p.name === opParam.name && p.in === opParam.in
6851
+ );
6852
+ if (existingIndex >= 0) {
6853
+ merged[existingIndex] = opParam;
6854
+ } else {
6855
+ merged.push(opParam);
6856
+ }
6857
+ }
6858
+ return merged;
6859
+ }
6860
+
6576
6861
  // src/openapi-generator.ts
6577
6862
  var OpenApiGenerator = class {
6578
6863
  constructor(options) {
@@ -6582,7 +6867,7 @@ var OpenApiGenerator = class {
6582
6867
  this.schemaUsageMap = /* @__PURE__ */ new Map();
6583
6868
  this.needsZodImport = true;
6584
6869
  this.filterStats = createFilterStatistics();
6585
- var _a, _b, _c, _d, _e;
6870
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
6586
6871
  if (!options.input) {
6587
6872
  throw new ConfigurationError("Input path is required", { providedOptions: options });
6588
6873
  }
@@ -6592,21 +6877,28 @@ var OpenApiGenerator = class {
6592
6877
  output: options.output,
6593
6878
  includeDescriptions: (_a = options.includeDescriptions) != null ? _a : true,
6594
6879
  useDescribe: (_b = options.useDescribe) != null ? _b : false,
6880
+ defaultNullable: (_c = options.defaultNullable) != null ? _c : false,
6881
+ emptyObjectBehavior: (_d = options.emptyObjectBehavior) != null ? _d : "loose",
6595
6882
  schemaType: options.schemaType || "all",
6596
6883
  prefix: options.prefix,
6597
6884
  suffix: options.suffix,
6598
6885
  stripSchemaPrefix: options.stripSchemaPrefix,
6599
- showStats: (_c = options.showStats) != null ? _c : true,
6886
+ stripPathPrefix: options.stripPathPrefix,
6887
+ showStats: (_e = options.showStats) != null ? _e : true,
6600
6888
  request: options.request,
6601
6889
  response: options.response,
6602
6890
  operationFilters: options.operationFilters,
6603
6891
  ignoreHeaders: options.ignoreHeaders,
6604
- cacheSize: (_d = options.cacheSize) != null ? _d : 1e3,
6605
- batchSize: (_e = options.batchSize) != null ? _e : 10
6892
+ cacheSize: (_f = options.cacheSize) != null ? _f : 1e3,
6893
+ batchSize: (_g = options.batchSize) != null ? _g : 10,
6894
+ customDateTimeFormatRegex: options.customDateTimeFormatRegex
6606
6895
  };
6607
6896
  if (this.options.cacheSize) {
6608
6897
  configurePatternCache(this.options.cacheSize);
6609
6898
  }
6899
+ if (this.options.customDateTimeFormatRegex) {
6900
+ configureDateTimeFormat(this.options.customDateTimeFormatRegex);
6901
+ }
6610
6902
  try {
6611
6903
  const fs = require("fs");
6612
6904
  if (!fs.existsSync(this.options.input)) {
@@ -6669,6 +6961,8 @@ var OpenApiGenerator = class {
6669
6961
  mode: this.requestOptions.mode,
6670
6962
  includeDescriptions: this.requestOptions.includeDescriptions,
6671
6963
  useDescribe: this.requestOptions.useDescribe,
6964
+ defaultNullable: (_h = this.options.defaultNullable) != null ? _h : false,
6965
+ emptyObjectBehavior: (_i = this.options.emptyObjectBehavior) != null ? _i : "loose",
6672
6966
  namingOptions: {
6673
6967
  prefix: this.options.prefix,
6674
6968
  suffix: this.options.suffix
@@ -7022,7 +7316,7 @@ var OpenApiGenerator = class {
7022
7316
  * Generate schema for a component
7023
7317
  */
7024
7318
  generateComponentSchema(name, schema) {
7025
- var _a, _b;
7319
+ var _a, _b, _c, _d;
7026
7320
  if (!this.schemaDependencies.has(name)) {
7027
7321
  this.schemaDependencies.set(name, /* @__PURE__ */ new Set());
7028
7322
  }
@@ -7054,14 +7348,15 @@ ${typeCode}`;
7054
7348
  mode: resolvedOptions.mode,
7055
7349
  includeDescriptions: resolvedOptions.includeDescriptions,
7056
7350
  useDescribe: resolvedOptions.useDescribe,
7351
+ defaultNullable: (_b = this.options.defaultNullable) != null ? _b : false,
7352
+ emptyObjectBehavior: (_c = this.options.emptyObjectBehavior) != null ? _c : "loose",
7057
7353
  namingOptions: {
7058
7354
  prefix: this.options.prefix,
7059
7355
  suffix: this.options.suffix
7060
7356
  },
7061
7357
  stripSchemaPrefix: this.options.stripSchemaPrefix
7062
7358
  });
7063
- const isAlias = !!(schema.$ref && !schema.properties && !schema.allOf && !schema.oneOf && !schema.anyOf);
7064
- const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, isAlias);
7359
+ const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, true);
7065
7360
  const zodSchemaCode = `${jsdoc}export const ${schemaName} = ${zodSchema};`;
7066
7361
  if (zodSchema.includes("z.discriminatedUnion(")) {
7067
7362
  const match = zodSchema.match(/z\.discriminatedUnion\([^,]+,\s*\[([^\]]+)\]/);
@@ -7071,7 +7366,7 @@ ${typeCode}`;
7071
7366
  const depMatch = ref.match(/^([a-z][a-zA-Z0-9]*?)Schema$/);
7072
7367
  if (depMatch) {
7073
7368
  const depName = depMatch[1].charAt(0).toUpperCase() + depMatch[1].slice(1);
7074
- (_b = this.schemaDependencies.get(name)) == null ? void 0 : _b.add(depName);
7369
+ (_d = this.schemaDependencies.get(name)) == null ? void 0 : _d.add(depName);
7075
7370
  }
7076
7371
  }
7077
7372
  }
@@ -7095,16 +7390,20 @@ ${typeCode}`;
7095
7390
  if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
7096
7391
  continue;
7097
7392
  }
7098
- if (!operation.operationId || !operation.parameters || !Array.isArray(operation.parameters)) {
7099
- continue;
7100
- }
7101
- const queryParams = operation.parameters.filter(
7393
+ const allParams = mergeParameters(pathItem.parameters, operation.parameters, this.spec);
7394
+ const queryParams = allParams.filter(
7102
7395
  (param) => param && typeof param === "object" && param.in === "query"
7103
7396
  );
7104
7397
  if (queryParams.length === 0) {
7105
7398
  continue;
7106
7399
  }
7107
- const pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
7400
+ let pascalOperationId;
7401
+ if (operation.operationId) {
7402
+ pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
7403
+ } else {
7404
+ const strippedPath = stripPathPrefix(path, this.options.stripPathPrefix);
7405
+ pascalOperationId = this.generateMethodNameFromPath(method, strippedPath);
7406
+ }
7108
7407
  const schemaName = `${pascalOperationId}QueryParams`;
7109
7408
  if (!this.schemaDependencies.has(schemaName)) {
7110
7409
  this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
@@ -7152,8 +7451,9 @@ ${propsCode}
7152
7451
  const prefixedName = this.options.prefix ? `${toPascalCase(this.options.prefix)}${operationName}` : operationName;
7153
7452
  const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
7154
7453
  const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}QueryParamsSchema`;
7454
+ const jsdocOperationName = operation.operationId || `${method.toUpperCase()} ${path}`;
7155
7455
  const jsdoc = `/**
7156
- * Query parameters for ${operation.operationId}
7456
+ * Query parameters for ${jsdocOperationName}
7157
7457
  */
7158
7458
  `;
7159
7459
  const fullSchemaCode = `${jsdoc}export const ${camelCaseSchemaName} = ${schemaCode};`;
@@ -7162,6 +7462,35 @@ ${propsCode}
7162
7462
  }
7163
7463
  }
7164
7464
  }
7465
+ /**
7466
+ * Generate a PascalCase method name from HTTP method and path
7467
+ * Used as fallback when operationId is not available
7468
+ * @internal
7469
+ */
7470
+ generateMethodNameFromPath(method, path) {
7471
+ const segments = path.split("/").filter(Boolean).map((segment) => {
7472
+ if (segment.startsWith("{") && segment.endsWith("}")) {
7473
+ const paramName = segment.slice(1, -1);
7474
+ return `By${this.capitalizeSegment(paramName)}`;
7475
+ }
7476
+ return this.capitalizeSegment(segment);
7477
+ }).join("");
7478
+ const capitalizedMethod = method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();
7479
+ return `${capitalizedMethod}${segments}`;
7480
+ }
7481
+ /**
7482
+ * Capitalizes a path segment, handling special characters like dashes, underscores, and dots
7483
+ * @internal
7484
+ */
7485
+ capitalizeSegment(str) {
7486
+ if (str.includes("-") || str.includes("_") || str.includes(".")) {
7487
+ return str.split(/[-_.]/).map((part) => {
7488
+ if (!part) return "";
7489
+ return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
7490
+ }).join("");
7491
+ }
7492
+ return str.charAt(0).toUpperCase() + str.slice(1);
7493
+ }
7165
7494
  /**
7166
7495
  * Check if a header should be ignored based on filter patterns
7167
7496
  * @internal
@@ -7198,16 +7527,20 @@ ${propsCode}
7198
7527
  if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
7199
7528
  continue;
7200
7529
  }
7201
- if (!operation.operationId || !operation.parameters || !Array.isArray(operation.parameters)) {
7202
- continue;
7203
- }
7204
- const headerParams = operation.parameters.filter(
7530
+ const allParams = mergeParameters(pathItem.parameters, operation.parameters, this.spec);
7531
+ const headerParams = allParams.filter(
7205
7532
  (param) => param && typeof param === "object" && param.in === "header" && !this.shouldIgnoreHeader(param.name)
7206
7533
  );
7207
7534
  if (headerParams.length === 0) {
7208
7535
  continue;
7209
7536
  }
7210
- const pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
7537
+ let pascalOperationId;
7538
+ if (operation.operationId) {
7539
+ pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
7540
+ } else {
7541
+ const strippedPath = stripPathPrefix(path, this.options.stripPathPrefix);
7542
+ pascalOperationId = this.generateMethodNameFromPath(method, strippedPath);
7543
+ }
7211
7544
  const schemaName = `${pascalOperationId}HeaderParams`;
7212
7545
  if (!this.schemaDependencies.has(schemaName)) {
7213
7546
  this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
@@ -7244,8 +7577,9 @@ ${propsCode}
7244
7577
  const prefixedName = this.options.prefix ? `${toPascalCase(this.options.prefix)}${operationName}` : operationName;
7245
7578
  const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
7246
7579
  const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}HeaderParamsSchema`;
7580
+ const jsdocOperationName = operation.operationId || `${method.toUpperCase()} ${path}`;
7247
7581
  const jsdoc = `/**
7248
- * Header parameters for ${operation.operationId}
7582
+ * Header parameters for ${jsdocOperationName}
7249
7583
  */
7250
7584
  `;
7251
7585
  const fullSchemaCode = `${jsdoc}export const ${camelCaseSchemaName} = ${schemaCode};`;
@@ -7284,13 +7618,23 @@ ${propsCode}
7284
7618
  }
7285
7619
  const type = schema.type;
7286
7620
  if (type === "string") {
7621
+ const formatMap = {
7622
+ email: "z.email()",
7623
+ uri: "z.url()",
7624
+ url: "z.url()",
7625
+ uuid: "z.uuid()"
7626
+ };
7627
+ if (schema.format && formatMap[schema.format]) {
7628
+ let zodType2 = formatMap[schema.format];
7629
+ if (schema.minLength !== void 0) zodType2 = `${zodType2}.min(${schema.minLength})`;
7630
+ if (schema.maxLength !== void 0) zodType2 = `${zodType2}.max(${schema.maxLength})`;
7631
+ if (schema.pattern) zodType2 = `${zodType2}.regex(/${schema.pattern}/)`;
7632
+ return zodType2;
7633
+ }
7287
7634
  let zodType = "z.string()";
7288
7635
  if (schema.minLength !== void 0) zodType = `${zodType}.min(${schema.minLength})`;
7289
7636
  if (schema.maxLength !== void 0) zodType = `${zodType}.max(${schema.maxLength})`;
7290
7637
  if (schema.pattern) zodType = `${zodType}.regex(/${schema.pattern}/)`;
7291
- if (schema.format === "email") zodType = `${zodType}.email()`;
7292
- if (schema.format === "uri" || schema.format === "url") zodType = `${zodType}.url()`;
7293
- if (schema.format === "uuid") zodType = `${zodType}.uuid()`;
7294
7638
  return zodType;
7295
7639
  }
7296
7640
  if (type === "number" || type === "integer") {
@@ -7315,11 +7659,6 @@ ${propsCode}
7315
7659
  }
7316
7660
  return "z.unknown()";
7317
7661
  }
7318
- // REMOVED: generateNativeEnum method - no longer needed as we only generate Zod schemas
7319
- // REMOVED: toEnumKey method - was only used by generateNativeEnum
7320
- // REMOVED: addConstraintsToJSDoc method - was only used for native TypeScript types
7321
- // REMOVED: generateNativeTypeDefinition method - was only used for native TypeScript types
7322
- // REMOVED: generateObjectType method - was only used for native TypeScript types
7323
7662
  /**
7324
7663
  * Topological sort for schema dependencies
7325
7664
  * Returns schemas in the order they should be declared
@@ -7425,7 +7764,8 @@ var import_zod = require("zod");
7425
7764
  var RequestResponseOptionsSchema = import_zod.z.strictObject({
7426
7765
  mode: import_zod.z.enum(["strict", "normal", "loose"]).optional(),
7427
7766
  useDescribe: import_zod.z.boolean().optional(),
7428
- includeDescriptions: import_zod.z.boolean().optional()
7767
+ includeDescriptions: import_zod.z.boolean().optional(),
7768
+ defaultNullable: import_zod.z.boolean().optional()
7429
7769
  });
7430
7770
  var OperationFiltersSchema = import_zod.z.strictObject({
7431
7771
  includeTags: import_zod.z.array(import_zod.z.string()).optional(),
@@ -7508,6 +7848,7 @@ var OpenApiGeneratorOptionsSchema = import_zod2.z.strictObject({
7508
7848
  output: import_zod2.z.string(),
7509
7849
  includeDescriptions: import_zod2.z.boolean().optional(),
7510
7850
  useDescribe: import_zod2.z.boolean().optional(),
7851
+ defaultNullable: import_zod2.z.boolean().optional(),
7511
7852
  schemaType: import_zod2.z.enum(["all", "request", "response"]).optional(),
7512
7853
  prefix: import_zod2.z.string().optional(),
7513
7854
  suffix: import_zod2.z.string().optional(),
@@ -7518,13 +7859,28 @@ var OpenApiGeneratorOptionsSchema = import_zod2.z.strictObject({
7518
7859
  name: import_zod2.z.string().optional(),
7519
7860
  operationFilters: OperationFiltersSchema.optional(),
7520
7861
  cacheSize: import_zod2.z.number().positive().optional(),
7521
- batchSize: import_zod2.z.number().positive().optional()
7862
+ batchSize: import_zod2.z.number().positive().optional(),
7863
+ customDateTimeFormatRegex: import_zod2.z.union([
7864
+ import_zod2.z.string().refine(
7865
+ (pattern) => {
7866
+ try {
7867
+ new RegExp(pattern);
7868
+ return true;
7869
+ } catch {
7870
+ return false;
7871
+ }
7872
+ },
7873
+ { message: "Must be a valid regular expression pattern" }
7874
+ ),
7875
+ import_zod2.z.instanceof(RegExp)
7876
+ ]).optional()
7522
7877
  });
7523
7878
  var ConfigFileSchema = import_zod2.z.strictObject({
7524
7879
  defaults: import_zod2.z.strictObject({
7525
7880
  mode: import_zod2.z.enum(["strict", "normal", "loose"]).optional(),
7526
7881
  includeDescriptions: import_zod2.z.boolean().optional(),
7527
7882
  useDescribe: import_zod2.z.boolean().optional(),
7883
+ defaultNullable: import_zod2.z.boolean().optional(),
7528
7884
  schemaType: import_zod2.z.enum(["all", "request", "response"]).optional(),
7529
7885
  prefix: import_zod2.z.string().optional(),
7530
7886
  suffix: import_zod2.z.string().optional(),
@@ -7534,7 +7890,21 @@ var ConfigFileSchema = import_zod2.z.strictObject({
7534
7890
  response: RequestResponseOptionsSchema.optional(),
7535
7891
  operationFilters: OperationFiltersSchema.optional(),
7536
7892
  cacheSize: import_zod2.z.number().positive().optional(),
7537
- batchSize: import_zod2.z.number().positive().optional()
7893
+ batchSize: import_zod2.z.number().positive().optional(),
7894
+ customDateTimeFormatRegex: import_zod2.z.union([
7895
+ import_zod2.z.string().refine(
7896
+ (pattern) => {
7897
+ try {
7898
+ new RegExp(pattern);
7899
+ return true;
7900
+ } catch {
7901
+ return false;
7902
+ }
7903
+ },
7904
+ { message: "Must be a valid regular expression pattern" }
7905
+ ),
7906
+ import_zod2.z.instanceof(RegExp)
7907
+ ]).optional()
7538
7908
  }).optional(),
7539
7909
  specs: import_zod2.z.array(OpenApiGeneratorOptionsSchema).min(1, {
7540
7910
  message: "Configuration must include at least one specification. Each specification should have 'input' and 'output' paths."
@@ -7583,10 +7953,12 @@ function mergeConfigWithDefaults(config) {
7583
7953
  mode: defaults.mode,
7584
7954
  includeDescriptions: defaults.includeDescriptions,
7585
7955
  useDescribe: defaults.useDescribe,
7956
+ defaultNullable: defaults.defaultNullable,
7586
7957
  schemaType: defaults.schemaType,
7587
7958
  prefix: defaults.prefix,
7588
7959
  suffix: defaults.suffix,
7589
7960
  showStats: defaults.showStats,
7961
+ customDateTimeFormatRegex: defaults.customDateTimeFormatRegex,
7590
7962
  // Override with spec-specific values (including required input/output)
7591
7963
  ...spec
7592
7964
  };