@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/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { O as OpenApiGeneratorOptions } from './types-B7ePTDjr.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-B7ePTDjr.mjs';
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';
3
3
 
4
4
  /**
5
5
  * Custom error classes for better error handling and debugging
@@ -125,6 +125,17 @@ declare class OpenApiGenerator {
125
125
  * Generate query parameter schemas for each operation
126
126
  */
127
127
  private generateQueryParameterSchemas;
128
+ /**
129
+ * Generate a PascalCase method name from HTTP method and path
130
+ * Used as fallback when operationId is not available
131
+ * @internal
132
+ */
133
+ private generateMethodNameFromPath;
134
+ /**
135
+ * Capitalizes a path segment, handling special characters like dashes, underscores, and dots
136
+ * @internal
137
+ */
138
+ private capitalizeSegment;
128
139
  /**
129
140
  * Check if a header should be ignored based on filter patterns
130
141
  * @internal
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { O as OpenApiGeneratorOptions } from './types-B7ePTDjr.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-B7ePTDjr.js';
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';
3
3
 
4
4
  /**
5
5
  * Custom error classes for better error handling and debugging
@@ -125,6 +125,17 @@ declare class OpenApiGenerator {
125
125
  * Generate query parameter schemas for each operation
126
126
  */
127
127
  private generateQueryParameterSchemas;
128
+ /**
129
+ * Generate a PascalCase method name from HTTP method and path
130
+ * Used as fallback when operationId is not available
131
+ * @internal
132
+ */
133
+ private generateMethodNameFromPath;
134
+ /**
135
+ * Capitalizes a path segment, handling special characters like dashes, underscores, and dots
136
+ * @internal
137
+ */
138
+ private capitalizeSegment;
128
139
  /**
129
140
  * Check if a header should be ignored based on filter patterns
130
141
  * @internal
package/dist/index.js CHANGED
@@ -94,7 +94,7 @@ var ConfigurationError = class extends GeneratorError {
94
94
  // src/openapi-generator.ts
95
95
  var import_node_fs = require("fs");
96
96
  var import_node_path = require("path");
97
- var import_minimatch2 = require("minimatch");
97
+ var import_minimatch3 = require("minimatch");
98
98
  var import_yaml = require("yaml");
99
99
 
100
100
  // src/utils/name-utils.ts
@@ -153,8 +153,26 @@ function resolveRef(ref) {
153
153
  function generateEnum(name, values, options) {
154
154
  const schemaName = `${toCamelCase(name, options)}Schema`;
155
155
  const typeName = toPascalCase(name);
156
- const enumValues = values.map((v) => `"${v}"`).join(", ");
157
- const schemaCode = `export const ${schemaName} = z.enum([${enumValues}]);`;
156
+ const allBooleans = values.every((v) => typeof v === "boolean");
157
+ if (allBooleans) {
158
+ const schemaCode2 = `export const ${schemaName} = z.boolean();`;
159
+ const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
160
+ return { schemaCode: schemaCode2, typeCode: typeCode2 };
161
+ }
162
+ const allStrings = values.every((v) => typeof v === "string");
163
+ if (allStrings) {
164
+ const enumValues = values.map((v) => `"${v}"`).join(", ");
165
+ const schemaCode2 = `export const ${schemaName} = z.enum([${enumValues}]);`;
166
+ const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
167
+ return { schemaCode: schemaCode2, typeCode: typeCode2 };
168
+ }
169
+ const literalValues = values.map((v) => {
170
+ if (typeof v === "string") {
171
+ return `z.literal("${v}")`;
172
+ }
173
+ return `z.literal(${v})`;
174
+ }).join(", ");
175
+ const schemaCode = `export const ${schemaName} = z.union([${literalValues}]);`;
158
176
  const typeCode = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
159
177
  return { schemaCode, typeCode };
160
178
  }
@@ -164,7 +182,7 @@ function escapeDescription(str) {
164
182
  return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
165
183
  }
166
184
  function escapePattern(str) {
167
- return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
185
+ return str.replace(/\//g, "\\/");
168
186
  }
169
187
  function escapeJSDoc(str) {
170
188
  return str.replace(/\*\//g, "*\\/");
@@ -172,14 +190,17 @@ function escapeJSDoc(str) {
172
190
  function wrapNullable(validation, isNullable2) {
173
191
  return isNullable2 ? `${validation}.nullable()` : validation;
174
192
  }
175
- function isNullable(schema) {
193
+ function isNullable(schema, defaultNullable = false) {
176
194
  if (schema.nullable === true) {
177
195
  return true;
178
196
  }
197
+ if (schema.nullable === false) {
198
+ return false;
199
+ }
179
200
  if (Array.isArray(schema.type)) {
180
201
  return schema.type.includes("null");
181
202
  }
182
- return false;
203
+ return defaultNullable;
183
204
  }
184
205
  function getPrimaryType(schema) {
185
206
  if (Array.isArray(schema.type)) {
@@ -289,61 +310,36 @@ var LRUCache = class {
289
310
  };
290
311
 
291
312
  // src/utils/pattern-utils.ts
292
- function isRegexPattern(pattern) {
293
- if (pattern.startsWith("^") || pattern.endsWith("$")) {
294
- return true;
295
- }
296
- if (/\\[dDwWsS]/.test(pattern)) {
297
- return true;
298
- }
299
- if (/\.\*|\.\+/.test(pattern)) {
300
- return true;
301
- }
302
- if (/[[\]()]/.test(pattern)) {
303
- return true;
304
- }
305
- if (/[^/][+?*]\{/.test(pattern)) {
313
+ var import_minimatch = require("minimatch");
314
+ function isValidGlobPattern(pattern) {
315
+ try {
316
+ new import_minimatch.minimatch.Minimatch(pattern);
306
317
  return true;
318
+ } catch {
319
+ return false;
307
320
  }
308
- return false;
309
321
  }
310
- function patternToRegex(pattern) {
311
- if (pattern instanceof RegExp) {
312
- return pattern;
313
- }
314
- if (isRegexPattern(pattern)) {
315
- try {
316
- return new RegExp(pattern);
317
- } catch (error) {
318
- console.warn(`\u26A0\uFE0F Invalid regex pattern "${pattern}": ${error instanceof Error ? error.message : String(error)}`);
319
- return null;
320
- }
321
- }
322
- return null;
322
+ function isGlobPattern(pattern) {
323
+ return /[*?[\]{}!]/.test(pattern);
323
324
  }
324
325
  function stripPrefix(input, pattern, ensureLeadingChar) {
325
326
  if (!pattern) {
326
327
  return input;
327
328
  }
328
- const regex = patternToRegex(pattern);
329
- if (regex) {
330
- const match = input.match(regex);
331
- if (match && match.index === 0) {
332
- const stripped = input.substring(match[0].length);
333
- if (ensureLeadingChar) {
334
- if (stripped === "") {
335
- return ensureLeadingChar;
336
- }
337
- if (!stripped.startsWith(ensureLeadingChar)) {
338
- return `${ensureLeadingChar}${stripped}`;
339
- }
329
+ if (isGlobPattern(pattern) && !isValidGlobPattern(pattern)) {
330
+ console.warn(`\u26A0\uFE0F Invalid glob pattern "${pattern}": Pattern is malformed`);
331
+ return input;
332
+ }
333
+ if (isGlobPattern(pattern)) {
334
+ let longestMatch = -1;
335
+ for (let i = 1; i <= input.length; i++) {
336
+ const testPrefix = input.substring(0, i);
337
+ if ((0, import_minimatch.minimatch)(testPrefix, pattern)) {
338
+ longestMatch = i;
340
339
  }
341
- return stripped;
342
340
  }
343
- } else {
344
- const stringPattern = pattern;
345
- if (input.startsWith(stringPattern)) {
346
- const stripped = input.substring(stringPattern.length);
341
+ if (longestMatch > 0) {
342
+ const stripped = input.substring(longestMatch);
347
343
  if (ensureLeadingChar) {
348
344
  if (stripped === "") {
349
345
  return ensureLeadingChar;
@@ -352,11 +348,40 @@ function stripPrefix(input, pattern, ensureLeadingChar) {
352
348
  return `${ensureLeadingChar}${stripped}`;
353
349
  }
354
350
  }
355
- return stripped;
351
+ return stripped === "" && !ensureLeadingChar ? input : stripped;
356
352
  }
353
+ return input;
354
+ }
355
+ if (input.startsWith(pattern)) {
356
+ const stripped = input.substring(pattern.length);
357
+ if (ensureLeadingChar) {
358
+ if (stripped === "") {
359
+ return ensureLeadingChar;
360
+ }
361
+ if (!stripped.startsWith(ensureLeadingChar)) {
362
+ return `${ensureLeadingChar}${stripped}`;
363
+ }
364
+ }
365
+ return stripped;
357
366
  }
358
367
  return input;
359
368
  }
369
+ function stripPathPrefix(path, pattern) {
370
+ if (!pattern) {
371
+ return path;
372
+ }
373
+ if (!isGlobPattern(pattern)) {
374
+ let normalizedPattern = pattern.trim();
375
+ if (!normalizedPattern.startsWith("/")) {
376
+ normalizedPattern = `/${normalizedPattern}`;
377
+ }
378
+ if (normalizedPattern.endsWith("/") && normalizedPattern !== "/") {
379
+ normalizedPattern = normalizedPattern.slice(0, -1);
380
+ }
381
+ return stripPrefix(path, normalizedPattern, "/");
382
+ }
383
+ return stripPrefix(path, pattern, "/");
384
+ }
360
385
 
361
386
  // src/validators/array-validator.ts
362
387
  function generateArrayValidation(schema, context) {
@@ -882,7 +907,7 @@ function configurePatternCache(size) {
882
907
  PATTERN_CACHE = new LRUCache(size);
883
908
  }
884
909
  }
885
- var FORMAT_MAP = {
910
+ var DEFAULT_FORMAT_MAP = {
886
911
  uuid: "z.uuid()",
887
912
  email: "z.email()",
888
913
  uri: "z.url()",
@@ -892,7 +917,6 @@ var FORMAT_MAP = {
892
917
  byte: "z.base64()",
893
918
  binary: "z.string()",
894
919
  date: "z.iso.date()",
895
- "date-time": "z.iso.datetime()",
896
920
  time: "z.iso.time()",
897
921
  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" })',
898
922
  ipv4: "z.ipv4()",
@@ -911,6 +935,30 @@ var FORMAT_MAP = {
911
935
  "json-pointer": 'z.string().refine((val) => val === "" || /^(\\/([^~/]|~0|~1)+)+$/.test(val), { message: "Must be a valid JSON Pointer (RFC 6901)" })',
912
936
  "relative-json-pointer": 'z.string().refine((val) => /^(0|[1-9]\\d*)(#|(\\/([^~/]|~0|~1)+)*)$/.test(val), { message: "Must be a valid relative JSON Pointer" })'
913
937
  };
938
+ var FORMAT_MAP = {
939
+ ...DEFAULT_FORMAT_MAP,
940
+ "date-time": "z.iso.datetime()"
941
+ };
942
+ function configureDateTimeFormat(pattern) {
943
+ if (!pattern) {
944
+ FORMAT_MAP["date-time"] = "z.iso.datetime()";
945
+ return;
946
+ }
947
+ const patternStr = pattern instanceof RegExp ? pattern.source : pattern;
948
+ if (patternStr === "") {
949
+ FORMAT_MAP["date-time"] = "z.iso.datetime()";
950
+ return;
951
+ }
952
+ try {
953
+ new RegExp(patternStr);
954
+ } catch (error) {
955
+ throw new Error(
956
+ `Invalid regular expression pattern for customDateTimeFormatRegex: ${patternStr}. ${error instanceof Error ? error.message : "Pattern is malformed"}`
957
+ );
958
+ }
959
+ const escapedPattern = escapePattern(patternStr);
960
+ FORMAT_MAP["date-time"] = `z.string().regex(/${escapedPattern}/)`;
961
+ }
914
962
  function generateStringValidation(schema, useDescribe) {
915
963
  let validation = FORMAT_MAP[schema.format || ""] || "z.string()";
916
964
  if (schema.minLength !== void 0) {
@@ -1200,7 +1248,8 @@ var _PropertyGenerator = class _PropertyGenerator {
1200
1248
  if ((this.context.schemaType === "request" || this.context.schemaType === "response") && schema.properties) {
1201
1249
  schema = this.filterNestedProperties(schema);
1202
1250
  }
1203
- const nullable = isNullable(schema);
1251
+ const effectiveDefaultNullable = isTopLevel ? false : this.context.defaultNullable;
1252
+ const nullable = isNullable(schema, effectiveDefaultNullable);
1204
1253
  if (hasMultipleTypes(schema)) {
1205
1254
  const union = this.generateMultiTypeUnion(schema, currentSchema);
1206
1255
  return wrapNullable(union, nullable);
@@ -1228,9 +1277,25 @@ var _PropertyGenerator = class _PropertyGenerator {
1228
1277
  return wrapNullable(zodLiteral, nullable);
1229
1278
  }
1230
1279
  if (schema.enum) {
1231
- const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
1232
- const zodEnum = `z.enum([${enumValues}])`;
1233
- return wrapNullable(zodEnum, nullable);
1280
+ const allBooleans = schema.enum.every((v) => typeof v === "boolean");
1281
+ if (allBooleans) {
1282
+ const zodBoolean = "z.boolean()";
1283
+ return wrapNullable(zodBoolean, nullable);
1284
+ }
1285
+ const allStrings = schema.enum.every((v) => typeof v === "string");
1286
+ if (allStrings) {
1287
+ const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
1288
+ const zodEnum = `z.enum([${enumValues}])`;
1289
+ return wrapNullable(zodEnum, nullable);
1290
+ }
1291
+ const literalValues = schema.enum.map((v) => {
1292
+ if (typeof v === "string") {
1293
+ return `z.literal("${v}")`;
1294
+ }
1295
+ return `z.literal(${v})`;
1296
+ }).join(", ");
1297
+ const zodUnion = `z.union([${literalValues}])`;
1298
+ return wrapNullable(zodUnion, nullable);
1234
1299
  }
1235
1300
  if (schema.allOf) {
1236
1301
  let composition = generateAllOf(
@@ -1361,7 +1426,7 @@ _PropertyGenerator.INCLUSION_RULES = {
1361
1426
  var PropertyGenerator = _PropertyGenerator;
1362
1427
 
1363
1428
  // src/utils/operation-filters.ts
1364
- var import_minimatch = require("minimatch");
1429
+ var import_minimatch2 = require("minimatch");
1365
1430
  function createFilterStatistics() {
1366
1431
  return {
1367
1432
  totalOperations: 0,
@@ -1380,7 +1445,7 @@ function matchesAnyPattern(value, patterns) {
1380
1445
  if (!value) {
1381
1446
  return false;
1382
1447
  }
1383
- return patterns.some((pattern) => (0, import_minimatch.minimatch)(value, pattern));
1448
+ return patterns.some((pattern) => (0, import_minimatch2.minimatch)(value, pattern));
1384
1449
  }
1385
1450
  function containsAny(arr, values) {
1386
1451
  if (!values || values.length === 0) {
@@ -1503,7 +1568,7 @@ var OpenApiGenerator = class {
1503
1568
  this.schemaUsageMap = /* @__PURE__ */ new Map();
1504
1569
  this.needsZodImport = true;
1505
1570
  this.filterStats = createFilterStatistics();
1506
- var _a, _b, _c, _d, _e;
1571
+ var _a, _b, _c, _d, _e, _f, _g;
1507
1572
  if (!options.input) {
1508
1573
  throw new ConfigurationError("Input path is required", { providedOptions: options });
1509
1574
  }
@@ -1513,21 +1578,27 @@ var OpenApiGenerator = class {
1513
1578
  output: options.output,
1514
1579
  includeDescriptions: (_a = options.includeDescriptions) != null ? _a : true,
1515
1580
  useDescribe: (_b = options.useDescribe) != null ? _b : false,
1581
+ defaultNullable: (_c = options.defaultNullable) != null ? _c : false,
1516
1582
  schemaType: options.schemaType || "all",
1517
1583
  prefix: options.prefix,
1518
1584
  suffix: options.suffix,
1519
1585
  stripSchemaPrefix: options.stripSchemaPrefix,
1520
- showStats: (_c = options.showStats) != null ? _c : true,
1586
+ stripPathPrefix: options.stripPathPrefix,
1587
+ showStats: (_d = options.showStats) != null ? _d : true,
1521
1588
  request: options.request,
1522
1589
  response: options.response,
1523
1590
  operationFilters: options.operationFilters,
1524
1591
  ignoreHeaders: options.ignoreHeaders,
1525
- cacheSize: (_d = options.cacheSize) != null ? _d : 1e3,
1526
- batchSize: (_e = options.batchSize) != null ? _e : 10
1592
+ cacheSize: (_e = options.cacheSize) != null ? _e : 1e3,
1593
+ batchSize: (_f = options.batchSize) != null ? _f : 10,
1594
+ customDateTimeFormatRegex: options.customDateTimeFormatRegex
1527
1595
  };
1528
1596
  if (this.options.cacheSize) {
1529
1597
  configurePatternCache(this.options.cacheSize);
1530
1598
  }
1599
+ if (this.options.customDateTimeFormatRegex) {
1600
+ configureDateTimeFormat(this.options.customDateTimeFormatRegex);
1601
+ }
1531
1602
  try {
1532
1603
  const fs = require("fs");
1533
1604
  if (!fs.existsSync(this.options.input)) {
@@ -1590,6 +1661,7 @@ var OpenApiGenerator = class {
1590
1661
  mode: this.requestOptions.mode,
1591
1662
  includeDescriptions: this.requestOptions.includeDescriptions,
1592
1663
  useDescribe: this.requestOptions.useDescribe,
1664
+ defaultNullable: (_g = this.options.defaultNullable) != null ? _g : false,
1593
1665
  namingOptions: {
1594
1666
  prefix: this.options.prefix,
1595
1667
  suffix: this.options.suffix
@@ -1607,6 +1679,9 @@ var OpenApiGenerator = class {
1607
1679
  throw new SpecValidationError("No schemas found in OpenAPI spec", { filePath: this.options.input });
1608
1680
  }
1609
1681
  for (const [name, schema] of Object.entries(this.spec.components.schemas)) {
1682
+ if (this.options.operationFilters && this.schemaUsageMap.size > 0 && !this.schemaUsageMap.has(name)) {
1683
+ continue;
1684
+ }
1610
1685
  this.generateComponentSchema(name, schema);
1611
1686
  }
1612
1687
  this.generateQueryParameterSchemas();
@@ -1940,7 +2015,7 @@ var OpenApiGenerator = class {
1940
2015
  * Generate schema for a component
1941
2016
  */
1942
2017
  generateComponentSchema(name, schema) {
1943
- var _a, _b;
2018
+ var _a, _b, _c;
1944
2019
  if (!this.schemaDependencies.has(name)) {
1945
2020
  this.schemaDependencies.set(name, /* @__PURE__ */ new Set());
1946
2021
  }
@@ -1972,14 +2047,14 @@ ${typeCode}`;
1972
2047
  mode: resolvedOptions.mode,
1973
2048
  includeDescriptions: resolvedOptions.includeDescriptions,
1974
2049
  useDescribe: resolvedOptions.useDescribe,
2050
+ defaultNullable: (_b = this.options.defaultNullable) != null ? _b : false,
1975
2051
  namingOptions: {
1976
2052
  prefix: this.options.prefix,
1977
2053
  suffix: this.options.suffix
1978
2054
  },
1979
2055
  stripSchemaPrefix: this.options.stripSchemaPrefix
1980
2056
  });
1981
- const isAlias = !!(schema.$ref && !schema.properties && !schema.allOf && !schema.oneOf && !schema.anyOf);
1982
- const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, isAlias);
2057
+ const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, true);
1983
2058
  const zodSchemaCode = `${jsdoc}export const ${schemaName} = ${zodSchema};`;
1984
2059
  if (zodSchema.includes("z.discriminatedUnion(")) {
1985
2060
  const match = zodSchema.match(/z\.discriminatedUnion\([^,]+,\s*\[([^\]]+)\]/);
@@ -1989,7 +2064,7 @@ ${typeCode}`;
1989
2064
  const depMatch = ref.match(/^([a-z][a-zA-Z0-9]*?)Schema$/);
1990
2065
  if (depMatch) {
1991
2066
  const depName = depMatch[1].charAt(0).toUpperCase() + depMatch[1].slice(1);
1992
- (_b = this.schemaDependencies.get(name)) == null ? void 0 : _b.add(depName);
2067
+ (_c = this.schemaDependencies.get(name)) == null ? void 0 : _c.add(depName);
1993
2068
  }
1994
2069
  }
1995
2070
  }
@@ -2013,7 +2088,7 @@ ${typeCode}`;
2013
2088
  if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
2014
2089
  continue;
2015
2090
  }
2016
- if (!operation.operationId || !operation.parameters || !Array.isArray(operation.parameters)) {
2091
+ if (!operation.parameters || !Array.isArray(operation.parameters)) {
2017
2092
  continue;
2018
2093
  }
2019
2094
  const queryParams = operation.parameters.filter(
@@ -2022,7 +2097,13 @@ ${typeCode}`;
2022
2097
  if (queryParams.length === 0) {
2023
2098
  continue;
2024
2099
  }
2025
- const pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
2100
+ let pascalOperationId;
2101
+ if (operation.operationId) {
2102
+ pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
2103
+ } else {
2104
+ const strippedPath = stripPathPrefix(path, this.options.stripPathPrefix);
2105
+ pascalOperationId = this.generateMethodNameFromPath(method, strippedPath);
2106
+ }
2026
2107
  const schemaName = `${pascalOperationId}QueryParams`;
2027
2108
  if (!this.schemaDependencies.has(schemaName)) {
2028
2109
  this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
@@ -2070,8 +2151,9 @@ ${propsCode}
2070
2151
  const prefixedName = this.options.prefix ? `${toPascalCase(this.options.prefix)}${operationName}` : operationName;
2071
2152
  const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
2072
2153
  const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}QueryParamsSchema`;
2154
+ const jsdocOperationName = operation.operationId || `${method.toUpperCase()} ${path}`;
2073
2155
  const jsdoc = `/**
2074
- * Query parameters for ${operation.operationId}
2156
+ * Query parameters for ${jsdocOperationName}
2075
2157
  */
2076
2158
  `;
2077
2159
  const fullSchemaCode = `${jsdoc}export const ${camelCaseSchemaName} = ${schemaCode};`;
@@ -2080,6 +2162,35 @@ ${propsCode}
2080
2162
  }
2081
2163
  }
2082
2164
  }
2165
+ /**
2166
+ * Generate a PascalCase method name from HTTP method and path
2167
+ * Used as fallback when operationId is not available
2168
+ * @internal
2169
+ */
2170
+ generateMethodNameFromPath(method, path) {
2171
+ const segments = path.split("/").filter(Boolean).map((segment) => {
2172
+ if (segment.startsWith("{") && segment.endsWith("}")) {
2173
+ const paramName = segment.slice(1, -1);
2174
+ return `By${this.capitalizeSegment(paramName)}`;
2175
+ }
2176
+ return this.capitalizeSegment(segment);
2177
+ }).join("");
2178
+ const capitalizedMethod = method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();
2179
+ return `${capitalizedMethod}${segments}`;
2180
+ }
2181
+ /**
2182
+ * Capitalizes a path segment, handling special characters like dashes, underscores, and dots
2183
+ * @internal
2184
+ */
2185
+ capitalizeSegment(str) {
2186
+ if (str.includes("-") || str.includes("_") || str.includes(".")) {
2187
+ return str.split(/[-_.]/).map((part) => {
2188
+ if (!part) return "";
2189
+ return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
2190
+ }).join("");
2191
+ }
2192
+ return str.charAt(0).toUpperCase() + str.slice(1);
2193
+ }
2083
2194
  /**
2084
2195
  * Check if a header should be ignored based on filter patterns
2085
2196
  * @internal
@@ -2095,7 +2206,7 @@ ${propsCode}
2095
2206
  const headerLower = headerName.toLowerCase();
2096
2207
  return ignorePatterns.some((pattern) => {
2097
2208
  const patternLower = pattern.toLowerCase();
2098
- return (0, import_minimatch2.minimatch)(headerLower, patternLower);
2209
+ return (0, import_minimatch3.minimatch)(headerLower, patternLower);
2099
2210
  });
2100
2211
  }
2101
2212
  /**
@@ -2116,7 +2227,7 @@ ${propsCode}
2116
2227
  if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
2117
2228
  continue;
2118
2229
  }
2119
- if (!operation.operationId || !operation.parameters || !Array.isArray(operation.parameters)) {
2230
+ if (!operation.parameters || !Array.isArray(operation.parameters)) {
2120
2231
  continue;
2121
2232
  }
2122
2233
  const headerParams = operation.parameters.filter(
@@ -2125,7 +2236,13 @@ ${propsCode}
2125
2236
  if (headerParams.length === 0) {
2126
2237
  continue;
2127
2238
  }
2128
- const pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
2239
+ let pascalOperationId;
2240
+ if (operation.operationId) {
2241
+ pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
2242
+ } else {
2243
+ const strippedPath = stripPathPrefix(path, this.options.stripPathPrefix);
2244
+ pascalOperationId = this.generateMethodNameFromPath(method, strippedPath);
2245
+ }
2129
2246
  const schemaName = `${pascalOperationId}HeaderParams`;
2130
2247
  if (!this.schemaDependencies.has(schemaName)) {
2131
2248
  this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
@@ -2162,8 +2279,9 @@ ${propsCode}
2162
2279
  const prefixedName = this.options.prefix ? `${toPascalCase(this.options.prefix)}${operationName}` : operationName;
2163
2280
  const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
2164
2281
  const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}HeaderParamsSchema`;
2282
+ const jsdocOperationName = operation.operationId || `${method.toUpperCase()} ${path}`;
2165
2283
  const jsdoc = `/**
2166
- * Header parameters for ${operation.operationId}
2284
+ * Header parameters for ${jsdocOperationName}
2167
2285
  */
2168
2286
  `;
2169
2287
  const fullSchemaCode = `${jsdoc}export const ${camelCaseSchemaName} = ${schemaCode};`;
@@ -2183,8 +2301,22 @@ ${propsCode}
2183
2301
  return `${schemaName}Schema`;
2184
2302
  }
2185
2303
  if (schema.enum) {
2186
- const enumValues = schema.enum.map((v) => typeof v === "string" ? `"${v}"` : v).join(", ");
2187
- return `z.enum([${enumValues}])`;
2304
+ const allBooleans = schema.enum.every((v) => typeof v === "boolean");
2305
+ if (allBooleans) {
2306
+ return "z.boolean()";
2307
+ }
2308
+ const allStrings = schema.enum.every((v) => typeof v === "string");
2309
+ if (allStrings) {
2310
+ const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
2311
+ return `z.enum([${enumValues}])`;
2312
+ }
2313
+ const literalValues = schema.enum.map((v) => {
2314
+ if (typeof v === "string") {
2315
+ return `z.literal("${v}")`;
2316
+ }
2317
+ return `z.literal(${v})`;
2318
+ }).join(", ");
2319
+ return `z.union([${literalValues}])`;
2188
2320
  }
2189
2321
  const type = schema.type;
2190
2322
  if (type === "string") {