@cerios/openapi-to-zod 1.1.0 → 1.2.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.mjs CHANGED
@@ -5197,8 +5197,26 @@ var init_name_utils = __esm({
5197
5197
  function generateEnum(name, values, options) {
5198
5198
  const schemaName = `${toCamelCase(name, options)}Schema`;
5199
5199
  const typeName = toPascalCase(name);
5200
- const enumValues = values.map((v) => `"${v}"`).join(", ");
5201
- const schemaCode = `export const ${schemaName} = z.enum([${enumValues}]);`;
5200
+ const allBooleans = values.every((v) => typeof v === "boolean");
5201
+ if (allBooleans) {
5202
+ const schemaCode2 = `export const ${schemaName} = z.boolean();`;
5203
+ const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
5204
+ return { schemaCode: schemaCode2, typeCode: typeCode2 };
5205
+ }
5206
+ const allStrings = values.every((v) => typeof v === "string");
5207
+ if (allStrings) {
5208
+ const enumValues = values.map((v) => `"${v}"`).join(", ");
5209
+ const schemaCode2 = `export const ${schemaName} = z.enum([${enumValues}]);`;
5210
+ const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
5211
+ return { schemaCode: schemaCode2, typeCode: typeCode2 };
5212
+ }
5213
+ const literalValues = values.map((v) => {
5214
+ if (typeof v === "string") {
5215
+ return `z.literal("${v}")`;
5216
+ }
5217
+ return `z.literal(${v})`;
5218
+ }).join(", ");
5219
+ const schemaCode = `export const ${schemaName} = z.union([${literalValues}]);`;
5202
5220
  const typeCode = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
5203
5221
  return { schemaCode, typeCode };
5204
5222
  }
@@ -5215,7 +5233,7 @@ function escapeDescription(str) {
5215
5233
  return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
5216
5234
  }
5217
5235
  function escapePattern(str) {
5218
- return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
5236
+ return str.replace(/\//g, "\\/");
5219
5237
  }
5220
5238
  function escapeJSDoc(str) {
5221
5239
  return str.replace(/\*\//g, "*\\/");
@@ -5223,14 +5241,17 @@ function escapeJSDoc(str) {
5223
5241
  function wrapNullable(validation, isNullable2) {
5224
5242
  return isNullable2 ? `${validation}.nullable()` : validation;
5225
5243
  }
5226
- function isNullable(schema) {
5244
+ function isNullable(schema, defaultNullable = false) {
5227
5245
  if (schema.nullable === true) {
5228
5246
  return true;
5229
5247
  }
5248
+ if (schema.nullable === false) {
5249
+ return false;
5250
+ }
5230
5251
  if (Array.isArray(schema.type)) {
5231
5252
  return schema.type.includes("null");
5232
5253
  }
5233
- return false;
5254
+ return defaultNullable;
5234
5255
  }
5235
5256
  function getPrimaryType(schema) {
5236
5257
  if (Array.isArray(schema.type)) {
@@ -5360,61 +5381,36 @@ var init_lru_cache = __esm({
5360
5381
  });
5361
5382
 
5362
5383
  // src/utils/pattern-utils.ts
5363
- function isRegexPattern(pattern) {
5364
- if (pattern.startsWith("^") || pattern.endsWith("$")) {
5365
- return true;
5366
- }
5367
- if (/\\[dDwWsS]/.test(pattern)) {
5368
- return true;
5369
- }
5370
- if (/\.\*|\.\+/.test(pattern)) {
5371
- return true;
5372
- }
5373
- if (/[[\]()]/.test(pattern)) {
5374
- return true;
5375
- }
5376
- if (/[^/][+?*]\{/.test(pattern)) {
5384
+ import { minimatch } from "minimatch";
5385
+ function isValidGlobPattern(pattern) {
5386
+ try {
5387
+ new minimatch.Minimatch(pattern);
5377
5388
  return true;
5389
+ } catch {
5390
+ return false;
5378
5391
  }
5379
- return false;
5380
5392
  }
5381
- function patternToRegex(pattern) {
5382
- if (pattern instanceof RegExp) {
5383
- return pattern;
5384
- }
5385
- if (isRegexPattern(pattern)) {
5386
- try {
5387
- return new RegExp(pattern);
5388
- } catch (error) {
5389
- console.warn(`\u26A0\uFE0F Invalid regex pattern "${pattern}": ${error instanceof Error ? error.message : String(error)}`);
5390
- return null;
5391
- }
5392
- }
5393
- return null;
5393
+ function isGlobPattern(pattern) {
5394
+ return /[*?[\]{}!]/.test(pattern);
5394
5395
  }
5395
5396
  function stripPrefix(input, pattern, ensureLeadingChar) {
5396
5397
  if (!pattern) {
5397
5398
  return input;
5398
5399
  }
5399
- const regex = patternToRegex(pattern);
5400
- if (regex) {
5401
- const match = input.match(regex);
5402
- if (match && match.index === 0) {
5403
- const stripped = input.substring(match[0].length);
5404
- if (ensureLeadingChar) {
5405
- if (stripped === "") {
5406
- return ensureLeadingChar;
5407
- }
5408
- if (!stripped.startsWith(ensureLeadingChar)) {
5409
- return `${ensureLeadingChar}${stripped}`;
5410
- }
5400
+ if (isGlobPattern(pattern) && !isValidGlobPattern(pattern)) {
5401
+ console.warn(`\u26A0\uFE0F Invalid glob pattern "${pattern}": Pattern is malformed`);
5402
+ return input;
5403
+ }
5404
+ if (isGlobPattern(pattern)) {
5405
+ let longestMatch = -1;
5406
+ for (let i = 1; i <= input.length; i++) {
5407
+ const testPrefix = input.substring(0, i);
5408
+ if (minimatch(testPrefix, pattern)) {
5409
+ longestMatch = i;
5411
5410
  }
5412
- return stripped;
5413
5411
  }
5414
- } else {
5415
- const stringPattern = pattern;
5416
- if (input.startsWith(stringPattern)) {
5417
- const stripped = input.substring(stringPattern.length);
5412
+ if (longestMatch > 0) {
5413
+ const stripped = input.substring(longestMatch);
5418
5414
  if (ensureLeadingChar) {
5419
5415
  if (stripped === "") {
5420
5416
  return ensureLeadingChar;
@@ -5423,11 +5419,40 @@ function stripPrefix(input, pattern, ensureLeadingChar) {
5423
5419
  return `${ensureLeadingChar}${stripped}`;
5424
5420
  }
5425
5421
  }
5426
- return stripped;
5422
+ return stripped === "" && !ensureLeadingChar ? input : stripped;
5427
5423
  }
5424
+ return input;
5425
+ }
5426
+ if (input.startsWith(pattern)) {
5427
+ const stripped = input.substring(pattern.length);
5428
+ if (ensureLeadingChar) {
5429
+ if (stripped === "") {
5430
+ return ensureLeadingChar;
5431
+ }
5432
+ if (!stripped.startsWith(ensureLeadingChar)) {
5433
+ return `${ensureLeadingChar}${stripped}`;
5434
+ }
5435
+ }
5436
+ return stripped;
5428
5437
  }
5429
5438
  return input;
5430
5439
  }
5440
+ function stripPathPrefix(path2, pattern) {
5441
+ if (!pattern) {
5442
+ return path2;
5443
+ }
5444
+ if (!isGlobPattern(pattern)) {
5445
+ let normalizedPattern = pattern.trim();
5446
+ if (!normalizedPattern.startsWith("/")) {
5447
+ normalizedPattern = `/${normalizedPattern}`;
5448
+ }
5449
+ if (normalizedPattern.endsWith("/") && normalizedPattern !== "/") {
5450
+ normalizedPattern = normalizedPattern.slice(0, -1);
5451
+ }
5452
+ return stripPrefix(path2, normalizedPattern, "/");
5453
+ }
5454
+ return stripPrefix(path2, pattern, "/");
5455
+ }
5431
5456
  var init_pattern_utils = __esm({
5432
5457
  "src/utils/pattern-utils.ts"() {
5433
5458
  "use strict";
@@ -5993,6 +6018,26 @@ function configurePatternCache(size) {
5993
6018
  PATTERN_CACHE = new LRUCache(size);
5994
6019
  }
5995
6020
  }
6021
+ function configureDateTimeFormat(pattern) {
6022
+ if (!pattern) {
6023
+ FORMAT_MAP["date-time"] = "z.iso.datetime()";
6024
+ return;
6025
+ }
6026
+ const patternStr = pattern instanceof RegExp ? pattern.source : pattern;
6027
+ if (patternStr === "") {
6028
+ FORMAT_MAP["date-time"] = "z.iso.datetime()";
6029
+ return;
6030
+ }
6031
+ try {
6032
+ new RegExp(patternStr);
6033
+ } catch (error) {
6034
+ throw new Error(
6035
+ `Invalid regular expression pattern for customDateTimeFormatRegex: ${patternStr}. ${error instanceof Error ? error.message : "Pattern is malformed"}`
6036
+ );
6037
+ }
6038
+ const escapedPattern = escapePattern(patternStr);
6039
+ FORMAT_MAP["date-time"] = `z.string().regex(/${escapedPattern}/)`;
6040
+ }
5996
6041
  function generateStringValidation(schema, useDescribe) {
5997
6042
  let validation = FORMAT_MAP[schema.format || ""] || "z.string()";
5998
6043
  if (schema.minLength !== void 0) {
@@ -6058,7 +6103,7 @@ function generateStringValidation(schema, useDescribe) {
6058
6103
  }
6059
6104
  return addDescription(validation, schema.description, useDescribe);
6060
6105
  }
6061
- var PATTERN_CACHE, FORMAT_MAP;
6106
+ var PATTERN_CACHE, DEFAULT_FORMAT_MAP, FORMAT_MAP;
6062
6107
  var init_string_validator = __esm({
6063
6108
  "src/validators/string-validator.ts"() {
6064
6109
  "use strict";
@@ -6066,7 +6111,7 @@ var init_string_validator = __esm({
6066
6111
  init_lru_cache();
6067
6112
  init_string_utils();
6068
6113
  PATTERN_CACHE = new LRUCache(1e3);
6069
- FORMAT_MAP = {
6114
+ DEFAULT_FORMAT_MAP = {
6070
6115
  uuid: "z.uuid()",
6071
6116
  email: "z.email()",
6072
6117
  uri: "z.url()",
@@ -6076,7 +6121,6 @@ var init_string_validator = __esm({
6076
6121
  byte: "z.base64()",
6077
6122
  binary: "z.string()",
6078
6123
  date: "z.iso.date()",
6079
- "date-time": "z.iso.datetime()",
6080
6124
  time: "z.iso.time()",
6081
6125
  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" })',
6082
6126
  ipv4: "z.ipv4()",
@@ -6095,6 +6139,10 @@ var init_string_validator = __esm({
6095
6139
  "json-pointer": 'z.string().refine((val) => val === "" || /^(\\/([^~/]|~0|~1)+)+$/.test(val), { message: "Must be a valid JSON Pointer (RFC 6901)" })',
6096
6140
  "relative-json-pointer": 'z.string().refine((val) => /^(0|[1-9]\\d*)(#|(\\/([^~/]|~0|~1)+)*)$/.test(val), { message: "Must be a valid relative JSON Pointer" })'
6097
6141
  };
6142
+ FORMAT_MAP = {
6143
+ ...DEFAULT_FORMAT_MAP,
6144
+ "date-time": "z.iso.datetime()"
6145
+ };
6098
6146
  }
6099
6147
  });
6100
6148
 
@@ -6335,7 +6383,8 @@ var init_property_generator = __esm({
6335
6383
  if ((this.context.schemaType === "request" || this.context.schemaType === "response") && schema.properties) {
6336
6384
  schema = this.filterNestedProperties(schema);
6337
6385
  }
6338
- const nullable = isNullable(schema);
6386
+ const effectiveDefaultNullable = isTopLevel ? false : this.context.defaultNullable;
6387
+ const nullable = isNullable(schema, effectiveDefaultNullable);
6339
6388
  if (hasMultipleTypes(schema)) {
6340
6389
  const union = this.generateMultiTypeUnion(schema, currentSchema);
6341
6390
  return wrapNullable(union, nullable);
@@ -6363,9 +6412,25 @@ var init_property_generator = __esm({
6363
6412
  return wrapNullable(zodLiteral, nullable);
6364
6413
  }
6365
6414
  if (schema.enum) {
6366
- const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
6367
- const zodEnum = `z.enum([${enumValues}])`;
6368
- return wrapNullable(zodEnum, nullable);
6415
+ const allBooleans = schema.enum.every((v) => typeof v === "boolean");
6416
+ if (allBooleans) {
6417
+ const zodBoolean = "z.boolean()";
6418
+ return wrapNullable(zodBoolean, nullable);
6419
+ }
6420
+ const allStrings = schema.enum.every((v) => typeof v === "string");
6421
+ if (allStrings) {
6422
+ const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
6423
+ const zodEnum = `z.enum([${enumValues}])`;
6424
+ return wrapNullable(zodEnum, nullable);
6425
+ }
6426
+ const literalValues = schema.enum.map((v) => {
6427
+ if (typeof v === "string") {
6428
+ return `z.literal("${v}")`;
6429
+ }
6430
+ return `z.literal(${v})`;
6431
+ }).join(", ");
6432
+ const zodUnion = `z.union([${literalValues}])`;
6433
+ return wrapNullable(zodUnion, nullable);
6369
6434
  }
6370
6435
  if (schema.allOf) {
6371
6436
  let composition = generateAllOf(
@@ -6498,7 +6563,7 @@ var init_property_generator = __esm({
6498
6563
  });
6499
6564
 
6500
6565
  // src/utils/operation-filters.ts
6501
- import { minimatch } from "minimatch";
6566
+ import { minimatch as minimatch2 } from "minimatch";
6502
6567
  function createFilterStatistics() {
6503
6568
  return {
6504
6569
  totalOperations: 0,
@@ -6517,7 +6582,7 @@ function matchesAnyPattern(value, patterns) {
6517
6582
  if (!value) {
6518
6583
  return false;
6519
6584
  }
6520
- return patterns.some((pattern) => minimatch(value, pattern));
6585
+ return patterns.some((pattern) => minimatch2(value, pattern));
6521
6586
  }
6522
6587
  function containsAny(arr, values) {
6523
6588
  if (!values || values.length === 0) {
@@ -6640,7 +6705,7 @@ var init_operation_filters = __esm({
6640
6705
  // src/openapi-generator.ts
6641
6706
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
6642
6707
  import { dirname, normalize } from "path";
6643
- import { minimatch as minimatch2 } from "minimatch";
6708
+ import { minimatch as minimatch3 } from "minimatch";
6644
6709
  import { parse } from "yaml";
6645
6710
  var OpenApiGenerator;
6646
6711
  var init_openapi_generator = __esm({
@@ -6663,7 +6728,7 @@ var init_openapi_generator = __esm({
6663
6728
  this.schemaUsageMap = /* @__PURE__ */ new Map();
6664
6729
  this.needsZodImport = true;
6665
6730
  this.filterStats = createFilterStatistics();
6666
- var _a, _b, _c, _d, _e;
6731
+ var _a, _b, _c, _d, _e, _f, _g;
6667
6732
  if (!options.input) {
6668
6733
  throw new ConfigurationError("Input path is required", { providedOptions: options });
6669
6734
  }
@@ -6673,21 +6738,27 @@ var init_openapi_generator = __esm({
6673
6738
  output: options.output,
6674
6739
  includeDescriptions: (_a = options.includeDescriptions) != null ? _a : true,
6675
6740
  useDescribe: (_b = options.useDescribe) != null ? _b : false,
6741
+ defaultNullable: (_c = options.defaultNullable) != null ? _c : false,
6676
6742
  schemaType: options.schemaType || "all",
6677
6743
  prefix: options.prefix,
6678
6744
  suffix: options.suffix,
6679
6745
  stripSchemaPrefix: options.stripSchemaPrefix,
6680
- showStats: (_c = options.showStats) != null ? _c : true,
6746
+ stripPathPrefix: options.stripPathPrefix,
6747
+ showStats: (_d = options.showStats) != null ? _d : true,
6681
6748
  request: options.request,
6682
6749
  response: options.response,
6683
6750
  operationFilters: options.operationFilters,
6684
6751
  ignoreHeaders: options.ignoreHeaders,
6685
- cacheSize: (_d = options.cacheSize) != null ? _d : 1e3,
6686
- batchSize: (_e = options.batchSize) != null ? _e : 10
6752
+ cacheSize: (_e = options.cacheSize) != null ? _e : 1e3,
6753
+ batchSize: (_f = options.batchSize) != null ? _f : 10,
6754
+ customDateTimeFormatRegex: options.customDateTimeFormatRegex
6687
6755
  };
6688
6756
  if (this.options.cacheSize) {
6689
6757
  configurePatternCache(this.options.cacheSize);
6690
6758
  }
6759
+ if (this.options.customDateTimeFormatRegex) {
6760
+ configureDateTimeFormat(this.options.customDateTimeFormatRegex);
6761
+ }
6691
6762
  try {
6692
6763
  const fs = __require("fs");
6693
6764
  if (!fs.existsSync(this.options.input)) {
@@ -6750,6 +6821,7 @@ var init_openapi_generator = __esm({
6750
6821
  mode: this.requestOptions.mode,
6751
6822
  includeDescriptions: this.requestOptions.includeDescriptions,
6752
6823
  useDescribe: this.requestOptions.useDescribe,
6824
+ defaultNullable: (_g = this.options.defaultNullable) != null ? _g : false,
6753
6825
  namingOptions: {
6754
6826
  prefix: this.options.prefix,
6755
6827
  suffix: this.options.suffix
@@ -6767,6 +6839,9 @@ var init_openapi_generator = __esm({
6767
6839
  throw new SpecValidationError("No schemas found in OpenAPI spec", { filePath: this.options.input });
6768
6840
  }
6769
6841
  for (const [name, schema] of Object.entries(this.spec.components.schemas)) {
6842
+ if (this.options.operationFilters && this.schemaUsageMap.size > 0 && !this.schemaUsageMap.has(name)) {
6843
+ continue;
6844
+ }
6770
6845
  this.generateComponentSchema(name, schema);
6771
6846
  }
6772
6847
  this.generateQueryParameterSchemas();
@@ -7100,7 +7175,7 @@ var init_openapi_generator = __esm({
7100
7175
  * Generate schema for a component
7101
7176
  */
7102
7177
  generateComponentSchema(name, schema) {
7103
- var _a, _b;
7178
+ var _a, _b, _c;
7104
7179
  if (!this.schemaDependencies.has(name)) {
7105
7180
  this.schemaDependencies.set(name, /* @__PURE__ */ new Set());
7106
7181
  }
@@ -7132,14 +7207,14 @@ ${typeCode}`;
7132
7207
  mode: resolvedOptions.mode,
7133
7208
  includeDescriptions: resolvedOptions.includeDescriptions,
7134
7209
  useDescribe: resolvedOptions.useDescribe,
7210
+ defaultNullable: (_b = this.options.defaultNullable) != null ? _b : false,
7135
7211
  namingOptions: {
7136
7212
  prefix: this.options.prefix,
7137
7213
  suffix: this.options.suffix
7138
7214
  },
7139
7215
  stripSchemaPrefix: this.options.stripSchemaPrefix
7140
7216
  });
7141
- const isAlias = !!(schema.$ref && !schema.properties && !schema.allOf && !schema.oneOf && !schema.anyOf);
7142
- const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, isAlias);
7217
+ const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, true);
7143
7218
  const zodSchemaCode = `${jsdoc}export const ${schemaName} = ${zodSchema};`;
7144
7219
  if (zodSchema.includes("z.discriminatedUnion(")) {
7145
7220
  const match = zodSchema.match(/z\.discriminatedUnion\([^,]+,\s*\[([^\]]+)\]/);
@@ -7149,7 +7224,7 @@ ${typeCode}`;
7149
7224
  const depMatch = ref.match(/^([a-z][a-zA-Z0-9]*?)Schema$/);
7150
7225
  if (depMatch) {
7151
7226
  const depName = depMatch[1].charAt(0).toUpperCase() + depMatch[1].slice(1);
7152
- (_b = this.schemaDependencies.get(name)) == null ? void 0 : _b.add(depName);
7227
+ (_c = this.schemaDependencies.get(name)) == null ? void 0 : _c.add(depName);
7153
7228
  }
7154
7229
  }
7155
7230
  }
@@ -7173,7 +7248,7 @@ ${typeCode}`;
7173
7248
  if (!shouldIncludeOperation(operation, path2, method, this.options.operationFilters)) {
7174
7249
  continue;
7175
7250
  }
7176
- if (!operation.operationId || !operation.parameters || !Array.isArray(operation.parameters)) {
7251
+ if (!operation.parameters || !Array.isArray(operation.parameters)) {
7177
7252
  continue;
7178
7253
  }
7179
7254
  const queryParams = operation.parameters.filter(
@@ -7182,7 +7257,13 @@ ${typeCode}`;
7182
7257
  if (queryParams.length === 0) {
7183
7258
  continue;
7184
7259
  }
7185
- const pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
7260
+ let pascalOperationId;
7261
+ if (operation.operationId) {
7262
+ pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
7263
+ } else {
7264
+ const strippedPath = stripPathPrefix(path2, this.options.stripPathPrefix);
7265
+ pascalOperationId = this.generateMethodNameFromPath(method, strippedPath);
7266
+ }
7186
7267
  const schemaName = `${pascalOperationId}QueryParams`;
7187
7268
  if (!this.schemaDependencies.has(schemaName)) {
7188
7269
  this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
@@ -7230,8 +7311,9 @@ ${propsCode}
7230
7311
  const prefixedName = this.options.prefix ? `${toPascalCase(this.options.prefix)}${operationName}` : operationName;
7231
7312
  const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
7232
7313
  const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}QueryParamsSchema`;
7314
+ const jsdocOperationName = operation.operationId || `${method.toUpperCase()} ${path2}`;
7233
7315
  const jsdoc = `/**
7234
- * Query parameters for ${operation.operationId}
7316
+ * Query parameters for ${jsdocOperationName}
7235
7317
  */
7236
7318
  `;
7237
7319
  const fullSchemaCode = `${jsdoc}export const ${camelCaseSchemaName} = ${schemaCode};`;
@@ -7240,6 +7322,35 @@ ${propsCode}
7240
7322
  }
7241
7323
  }
7242
7324
  }
7325
+ /**
7326
+ * Generate a PascalCase method name from HTTP method and path
7327
+ * Used as fallback when operationId is not available
7328
+ * @internal
7329
+ */
7330
+ generateMethodNameFromPath(method, path2) {
7331
+ const segments = path2.split("/").filter(Boolean).map((segment) => {
7332
+ if (segment.startsWith("{") && segment.endsWith("}")) {
7333
+ const paramName = segment.slice(1, -1);
7334
+ return `By${this.capitalizeSegment(paramName)}`;
7335
+ }
7336
+ return this.capitalizeSegment(segment);
7337
+ }).join("");
7338
+ const capitalizedMethod = method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();
7339
+ return `${capitalizedMethod}${segments}`;
7340
+ }
7341
+ /**
7342
+ * Capitalizes a path segment, handling special characters like dashes, underscores, and dots
7343
+ * @internal
7344
+ */
7345
+ capitalizeSegment(str) {
7346
+ if (str.includes("-") || str.includes("_") || str.includes(".")) {
7347
+ return str.split(/[-_.]/).map((part) => {
7348
+ if (!part) return "";
7349
+ return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
7350
+ }).join("");
7351
+ }
7352
+ return str.charAt(0).toUpperCase() + str.slice(1);
7353
+ }
7243
7354
  /**
7244
7355
  * Check if a header should be ignored based on filter patterns
7245
7356
  * @internal
@@ -7255,7 +7366,7 @@ ${propsCode}
7255
7366
  const headerLower = headerName.toLowerCase();
7256
7367
  return ignorePatterns.some((pattern) => {
7257
7368
  const patternLower = pattern.toLowerCase();
7258
- return minimatch2(headerLower, patternLower);
7369
+ return minimatch3(headerLower, patternLower);
7259
7370
  });
7260
7371
  }
7261
7372
  /**
@@ -7276,7 +7387,7 @@ ${propsCode}
7276
7387
  if (!shouldIncludeOperation(operation, path2, method, this.options.operationFilters)) {
7277
7388
  continue;
7278
7389
  }
7279
- if (!operation.operationId || !operation.parameters || !Array.isArray(operation.parameters)) {
7390
+ if (!operation.parameters || !Array.isArray(operation.parameters)) {
7280
7391
  continue;
7281
7392
  }
7282
7393
  const headerParams = operation.parameters.filter(
@@ -7285,7 +7396,13 @@ ${propsCode}
7285
7396
  if (headerParams.length === 0) {
7286
7397
  continue;
7287
7398
  }
7288
- const pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
7399
+ let pascalOperationId;
7400
+ if (operation.operationId) {
7401
+ pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
7402
+ } else {
7403
+ const strippedPath = stripPathPrefix(path2, this.options.stripPathPrefix);
7404
+ pascalOperationId = this.generateMethodNameFromPath(method, strippedPath);
7405
+ }
7289
7406
  const schemaName = `${pascalOperationId}HeaderParams`;
7290
7407
  if (!this.schemaDependencies.has(schemaName)) {
7291
7408
  this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
@@ -7322,8 +7439,9 @@ ${propsCode}
7322
7439
  const prefixedName = this.options.prefix ? `${toPascalCase(this.options.prefix)}${operationName}` : operationName;
7323
7440
  const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
7324
7441
  const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}HeaderParamsSchema`;
7442
+ const jsdocOperationName = operation.operationId || `${method.toUpperCase()} ${path2}`;
7325
7443
  const jsdoc = `/**
7326
- * Header parameters for ${operation.operationId}
7444
+ * Header parameters for ${jsdocOperationName}
7327
7445
  */
7328
7446
  `;
7329
7447
  const fullSchemaCode = `${jsdoc}export const ${camelCaseSchemaName} = ${schemaCode};`;
@@ -7343,8 +7461,22 @@ ${propsCode}
7343
7461
  return `${schemaName}Schema`;
7344
7462
  }
7345
7463
  if (schema.enum) {
7346
- const enumValues = schema.enum.map((v) => typeof v === "string" ? `"${v}"` : v).join(", ");
7347
- return `z.enum([${enumValues}])`;
7464
+ const allBooleans = schema.enum.every((v) => typeof v === "boolean");
7465
+ if (allBooleans) {
7466
+ return "z.boolean()";
7467
+ }
7468
+ const allStrings = schema.enum.every((v) => typeof v === "string");
7469
+ if (allStrings) {
7470
+ const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
7471
+ return `z.enum([${enumValues}])`;
7472
+ }
7473
+ const literalValues = schema.enum.map((v) => {
7474
+ if (typeof v === "string") {
7475
+ return `z.literal("${v}")`;
7476
+ }
7477
+ return `z.literal(${v})`;
7478
+ }).join(", ");
7479
+ return `z.union([${literalValues}])`;
7348
7480
  }
7349
7481
  const type = schema.type;
7350
7482
  if (type === "string") {
@@ -7490,7 +7622,8 @@ var init_config_schemas = __esm({
7490
7622
  RequestResponseOptionsSchema = z.strictObject({
7491
7623
  mode: z.enum(["strict", "normal", "loose"]).optional(),
7492
7624
  useDescribe: z.boolean().optional(),
7493
- includeDescriptions: z.boolean().optional()
7625
+ includeDescriptions: z.boolean().optional(),
7626
+ defaultNullable: z.boolean().optional()
7494
7627
  });
7495
7628
  OperationFiltersSchema = z.strictObject({
7496
7629
  includeTags: z.array(z.string()).optional(),
@@ -7621,10 +7754,12 @@ function mergeConfigWithDefaults(config) {
7621
7754
  mode: defaults.mode,
7622
7755
  includeDescriptions: defaults.includeDescriptions,
7623
7756
  useDescribe: defaults.useDescribe,
7757
+ defaultNullable: defaults.defaultNullable,
7624
7758
  schemaType: defaults.schemaType,
7625
7759
  prefix: defaults.prefix,
7626
7760
  suffix: defaults.suffix,
7627
7761
  showStats: defaults.showStats,
7762
+ customDateTimeFormatRegex: defaults.customDateTimeFormatRegex,
7628
7763
  // Override with spec-specific values (including required input/output)
7629
7764
  ...spec
7630
7765
  };
@@ -7645,6 +7780,7 @@ var init_config_loader = __esm({
7645
7780
  output: z2.string(),
7646
7781
  includeDescriptions: z2.boolean().optional(),
7647
7782
  useDescribe: z2.boolean().optional(),
7783
+ defaultNullable: z2.boolean().optional(),
7648
7784
  schemaType: z2.enum(["all", "request", "response"]).optional(),
7649
7785
  prefix: z2.string().optional(),
7650
7786
  suffix: z2.string().optional(),
@@ -7655,13 +7791,28 @@ var init_config_loader = __esm({
7655
7791
  name: z2.string().optional(),
7656
7792
  operationFilters: OperationFiltersSchema.optional(),
7657
7793
  cacheSize: z2.number().positive().optional(),
7658
- batchSize: z2.number().positive().optional()
7794
+ batchSize: z2.number().positive().optional(),
7795
+ customDateTimeFormatRegex: z2.union([
7796
+ z2.string().refine(
7797
+ (pattern) => {
7798
+ try {
7799
+ new RegExp(pattern);
7800
+ return true;
7801
+ } catch {
7802
+ return false;
7803
+ }
7804
+ },
7805
+ { message: "Must be a valid regular expression pattern" }
7806
+ ),
7807
+ z2.instanceof(RegExp)
7808
+ ]).optional()
7659
7809
  });
7660
7810
  ConfigFileSchema = z2.strictObject({
7661
7811
  defaults: z2.strictObject({
7662
7812
  mode: z2.enum(["strict", "normal", "loose"]).optional(),
7663
7813
  includeDescriptions: z2.boolean().optional(),
7664
7814
  useDescribe: z2.boolean().optional(),
7815
+ defaultNullable: z2.boolean().optional(),
7665
7816
  schemaType: z2.enum(["all", "request", "response"]).optional(),
7666
7817
  prefix: z2.string().optional(),
7667
7818
  suffix: z2.string().optional(),
@@ -7671,7 +7822,21 @@ var init_config_loader = __esm({
7671
7822
  response: RequestResponseOptionsSchema.optional(),
7672
7823
  operationFilters: OperationFiltersSchema.optional(),
7673
7824
  cacheSize: z2.number().positive().optional(),
7674
- batchSize: z2.number().positive().optional()
7825
+ batchSize: z2.number().positive().optional(),
7826
+ customDateTimeFormatRegex: z2.union([
7827
+ z2.string().refine(
7828
+ (pattern) => {
7829
+ try {
7830
+ new RegExp(pattern);
7831
+ return true;
7832
+ } catch {
7833
+ return false;
7834
+ }
7835
+ },
7836
+ { message: "Must be a valid regular expression pattern" }
7837
+ ),
7838
+ z2.instanceof(RegExp)
7839
+ ]).optional()
7675
7840
  }).optional(),
7676
7841
  specs: z2.array(OpenApiGeneratorOptionsSchema).min(1, {
7677
7842
  message: "Configuration must include at least one specification. Each specification should have 'input' and 'output' paths."