@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.mjs CHANGED
@@ -67,7 +67,7 @@ var ConfigurationError = class extends GeneratorError {
67
67
  // src/openapi-generator.ts
68
68
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
69
69
  import { dirname, normalize } from "path";
70
- import { minimatch as minimatch2 } from "minimatch";
70
+ import { minimatch as minimatch3 } from "minimatch";
71
71
  import { parse } from "yaml";
72
72
 
73
73
  // src/utils/name-utils.ts
@@ -126,8 +126,26 @@ function resolveRef(ref) {
126
126
  function generateEnum(name, values, options) {
127
127
  const schemaName = `${toCamelCase(name, options)}Schema`;
128
128
  const typeName = toPascalCase(name);
129
- const enumValues = values.map((v) => `"${v}"`).join(", ");
130
- const schemaCode = `export const ${schemaName} = z.enum([${enumValues}]);`;
129
+ const allBooleans = values.every((v) => typeof v === "boolean");
130
+ if (allBooleans) {
131
+ const schemaCode2 = `export const ${schemaName} = z.boolean();`;
132
+ const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
133
+ return { schemaCode: schemaCode2, typeCode: typeCode2 };
134
+ }
135
+ const allStrings = values.every((v) => typeof v === "string");
136
+ if (allStrings) {
137
+ const enumValues = values.map((v) => `"${v}"`).join(", ");
138
+ const schemaCode2 = `export const ${schemaName} = z.enum([${enumValues}]);`;
139
+ const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
140
+ return { schemaCode: schemaCode2, typeCode: typeCode2 };
141
+ }
142
+ const literalValues = values.map((v) => {
143
+ if (typeof v === "string") {
144
+ return `z.literal("${v}")`;
145
+ }
146
+ return `z.literal(${v})`;
147
+ }).join(", ");
148
+ const schemaCode = `export const ${schemaName} = z.union([${literalValues}]);`;
131
149
  const typeCode = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
132
150
  return { schemaCode, typeCode };
133
151
  }
@@ -137,7 +155,7 @@ function escapeDescription(str) {
137
155
  return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
138
156
  }
139
157
  function escapePattern(str) {
140
- return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
158
+ return str.replace(/\//g, "\\/");
141
159
  }
142
160
  function escapeJSDoc(str) {
143
161
  return str.replace(/\*\//g, "*\\/");
@@ -145,14 +163,17 @@ function escapeJSDoc(str) {
145
163
  function wrapNullable(validation, isNullable2) {
146
164
  return isNullable2 ? `${validation}.nullable()` : validation;
147
165
  }
148
- function isNullable(schema) {
166
+ function isNullable(schema, defaultNullable = false) {
149
167
  if (schema.nullable === true) {
150
168
  return true;
151
169
  }
170
+ if (schema.nullable === false) {
171
+ return false;
172
+ }
152
173
  if (Array.isArray(schema.type)) {
153
174
  return schema.type.includes("null");
154
175
  }
155
- return false;
176
+ return defaultNullable;
156
177
  }
157
178
  function getPrimaryType(schema) {
158
179
  if (Array.isArray(schema.type)) {
@@ -262,61 +283,36 @@ var LRUCache = class {
262
283
  };
263
284
 
264
285
  // src/utils/pattern-utils.ts
265
- function isRegexPattern(pattern) {
266
- if (pattern.startsWith("^") || pattern.endsWith("$")) {
267
- return true;
268
- }
269
- if (/\\[dDwWsS]/.test(pattern)) {
270
- return true;
271
- }
272
- if (/\.\*|\.\+/.test(pattern)) {
273
- return true;
274
- }
275
- if (/[[\]()]/.test(pattern)) {
276
- return true;
277
- }
278
- if (/[^/][+?*]\{/.test(pattern)) {
286
+ import { minimatch } from "minimatch";
287
+ function isValidGlobPattern(pattern) {
288
+ try {
289
+ new minimatch.Minimatch(pattern);
279
290
  return true;
291
+ } catch {
292
+ return false;
280
293
  }
281
- return false;
282
294
  }
283
- function patternToRegex(pattern) {
284
- if (pattern instanceof RegExp) {
285
- return pattern;
286
- }
287
- if (isRegexPattern(pattern)) {
288
- try {
289
- return new RegExp(pattern);
290
- } catch (error) {
291
- console.warn(`\u26A0\uFE0F Invalid regex pattern "${pattern}": ${error instanceof Error ? error.message : String(error)}`);
292
- return null;
293
- }
294
- }
295
- return null;
295
+ function isGlobPattern(pattern) {
296
+ return /[*?[\]{}!]/.test(pattern);
296
297
  }
297
298
  function stripPrefix(input, pattern, ensureLeadingChar) {
298
299
  if (!pattern) {
299
300
  return input;
300
301
  }
301
- const regex = patternToRegex(pattern);
302
- if (regex) {
303
- const match = input.match(regex);
304
- if (match && match.index === 0) {
305
- const stripped = input.substring(match[0].length);
306
- if (ensureLeadingChar) {
307
- if (stripped === "") {
308
- return ensureLeadingChar;
309
- }
310
- if (!stripped.startsWith(ensureLeadingChar)) {
311
- return `${ensureLeadingChar}${stripped}`;
312
- }
302
+ if (isGlobPattern(pattern) && !isValidGlobPattern(pattern)) {
303
+ console.warn(`\u26A0\uFE0F Invalid glob pattern "${pattern}": Pattern is malformed`);
304
+ return input;
305
+ }
306
+ if (isGlobPattern(pattern)) {
307
+ let longestMatch = -1;
308
+ for (let i = 1; i <= input.length; i++) {
309
+ const testPrefix = input.substring(0, i);
310
+ if (minimatch(testPrefix, pattern)) {
311
+ longestMatch = i;
313
312
  }
314
- return stripped;
315
313
  }
316
- } else {
317
- const stringPattern = pattern;
318
- if (input.startsWith(stringPattern)) {
319
- const stripped = input.substring(stringPattern.length);
314
+ if (longestMatch > 0) {
315
+ const stripped = input.substring(longestMatch);
320
316
  if (ensureLeadingChar) {
321
317
  if (stripped === "") {
322
318
  return ensureLeadingChar;
@@ -325,11 +321,40 @@ function stripPrefix(input, pattern, ensureLeadingChar) {
325
321
  return `${ensureLeadingChar}${stripped}`;
326
322
  }
327
323
  }
328
- return stripped;
324
+ return stripped === "" && !ensureLeadingChar ? input : stripped;
329
325
  }
326
+ return input;
327
+ }
328
+ if (input.startsWith(pattern)) {
329
+ const stripped = input.substring(pattern.length);
330
+ if (ensureLeadingChar) {
331
+ if (stripped === "") {
332
+ return ensureLeadingChar;
333
+ }
334
+ if (!stripped.startsWith(ensureLeadingChar)) {
335
+ return `${ensureLeadingChar}${stripped}`;
336
+ }
337
+ }
338
+ return stripped;
330
339
  }
331
340
  return input;
332
341
  }
342
+ function stripPathPrefix(path, pattern) {
343
+ if (!pattern) {
344
+ return path;
345
+ }
346
+ if (!isGlobPattern(pattern)) {
347
+ let normalizedPattern = pattern.trim();
348
+ if (!normalizedPattern.startsWith("/")) {
349
+ normalizedPattern = `/${normalizedPattern}`;
350
+ }
351
+ if (normalizedPattern.endsWith("/") && normalizedPattern !== "/") {
352
+ normalizedPattern = normalizedPattern.slice(0, -1);
353
+ }
354
+ return stripPrefix(path, normalizedPattern, "/");
355
+ }
356
+ return stripPrefix(path, pattern, "/");
357
+ }
333
358
 
334
359
  // src/validators/array-validator.ts
335
360
  function generateArrayValidation(schema, context) {
@@ -855,7 +880,7 @@ function configurePatternCache(size) {
855
880
  PATTERN_CACHE = new LRUCache(size);
856
881
  }
857
882
  }
858
- var FORMAT_MAP = {
883
+ var DEFAULT_FORMAT_MAP = {
859
884
  uuid: "z.uuid()",
860
885
  email: "z.email()",
861
886
  uri: "z.url()",
@@ -865,7 +890,6 @@ var FORMAT_MAP = {
865
890
  byte: "z.base64()",
866
891
  binary: "z.string()",
867
892
  date: "z.iso.date()",
868
- "date-time": "z.iso.datetime()",
869
893
  time: "z.iso.time()",
870
894
  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" })',
871
895
  ipv4: "z.ipv4()",
@@ -884,6 +908,30 @@ var FORMAT_MAP = {
884
908
  "json-pointer": 'z.string().refine((val) => val === "" || /^(\\/([^~/]|~0|~1)+)+$/.test(val), { message: "Must be a valid JSON Pointer (RFC 6901)" })',
885
909
  "relative-json-pointer": 'z.string().refine((val) => /^(0|[1-9]\\d*)(#|(\\/([^~/]|~0|~1)+)*)$/.test(val), { message: "Must be a valid relative JSON Pointer" })'
886
910
  };
911
+ var FORMAT_MAP = {
912
+ ...DEFAULT_FORMAT_MAP,
913
+ "date-time": "z.iso.datetime()"
914
+ };
915
+ function configureDateTimeFormat(pattern) {
916
+ if (!pattern) {
917
+ FORMAT_MAP["date-time"] = "z.iso.datetime()";
918
+ return;
919
+ }
920
+ const patternStr = pattern instanceof RegExp ? pattern.source : pattern;
921
+ if (patternStr === "") {
922
+ FORMAT_MAP["date-time"] = "z.iso.datetime()";
923
+ return;
924
+ }
925
+ try {
926
+ new RegExp(patternStr);
927
+ } catch (error) {
928
+ throw new Error(
929
+ `Invalid regular expression pattern for customDateTimeFormatRegex: ${patternStr}. ${error instanceof Error ? error.message : "Pattern is malformed"}`
930
+ );
931
+ }
932
+ const escapedPattern = escapePattern(patternStr);
933
+ FORMAT_MAP["date-time"] = `z.string().regex(/${escapedPattern}/)`;
934
+ }
887
935
  function generateStringValidation(schema, useDescribe) {
888
936
  let validation = FORMAT_MAP[schema.format || ""] || "z.string()";
889
937
  if (schema.minLength !== void 0) {
@@ -1173,7 +1221,8 @@ var _PropertyGenerator = class _PropertyGenerator {
1173
1221
  if ((this.context.schemaType === "request" || this.context.schemaType === "response") && schema.properties) {
1174
1222
  schema = this.filterNestedProperties(schema);
1175
1223
  }
1176
- const nullable = isNullable(schema);
1224
+ const effectiveDefaultNullable = isTopLevel ? false : this.context.defaultNullable;
1225
+ const nullable = isNullable(schema, effectiveDefaultNullable);
1177
1226
  if (hasMultipleTypes(schema)) {
1178
1227
  const union = this.generateMultiTypeUnion(schema, currentSchema);
1179
1228
  return wrapNullable(union, nullable);
@@ -1201,9 +1250,25 @@ var _PropertyGenerator = class _PropertyGenerator {
1201
1250
  return wrapNullable(zodLiteral, nullable);
1202
1251
  }
1203
1252
  if (schema.enum) {
1204
- const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
1205
- const zodEnum = `z.enum([${enumValues}])`;
1206
- return wrapNullable(zodEnum, nullable);
1253
+ const allBooleans = schema.enum.every((v) => typeof v === "boolean");
1254
+ if (allBooleans) {
1255
+ const zodBoolean = "z.boolean()";
1256
+ return wrapNullable(zodBoolean, nullable);
1257
+ }
1258
+ const allStrings = schema.enum.every((v) => typeof v === "string");
1259
+ if (allStrings) {
1260
+ const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
1261
+ const zodEnum = `z.enum([${enumValues}])`;
1262
+ return wrapNullable(zodEnum, nullable);
1263
+ }
1264
+ const literalValues = schema.enum.map((v) => {
1265
+ if (typeof v === "string") {
1266
+ return `z.literal("${v}")`;
1267
+ }
1268
+ return `z.literal(${v})`;
1269
+ }).join(", ");
1270
+ const zodUnion = `z.union([${literalValues}])`;
1271
+ return wrapNullable(zodUnion, nullable);
1207
1272
  }
1208
1273
  if (schema.allOf) {
1209
1274
  let composition = generateAllOf(
@@ -1334,7 +1399,7 @@ _PropertyGenerator.INCLUSION_RULES = {
1334
1399
  var PropertyGenerator = _PropertyGenerator;
1335
1400
 
1336
1401
  // src/utils/operation-filters.ts
1337
- import { minimatch } from "minimatch";
1402
+ import { minimatch as minimatch2 } from "minimatch";
1338
1403
  function createFilterStatistics() {
1339
1404
  return {
1340
1405
  totalOperations: 0,
@@ -1353,7 +1418,7 @@ function matchesAnyPattern(value, patterns) {
1353
1418
  if (!value) {
1354
1419
  return false;
1355
1420
  }
1356
- return patterns.some((pattern) => minimatch(value, pattern));
1421
+ return patterns.some((pattern) => minimatch2(value, pattern));
1357
1422
  }
1358
1423
  function containsAny(arr, values) {
1359
1424
  if (!values || values.length === 0) {
@@ -1476,7 +1541,7 @@ var OpenApiGenerator = class {
1476
1541
  this.schemaUsageMap = /* @__PURE__ */ new Map();
1477
1542
  this.needsZodImport = true;
1478
1543
  this.filterStats = createFilterStatistics();
1479
- var _a, _b, _c, _d, _e;
1544
+ var _a, _b, _c, _d, _e, _f, _g;
1480
1545
  if (!options.input) {
1481
1546
  throw new ConfigurationError("Input path is required", { providedOptions: options });
1482
1547
  }
@@ -1486,21 +1551,27 @@ var OpenApiGenerator = class {
1486
1551
  output: options.output,
1487
1552
  includeDescriptions: (_a = options.includeDescriptions) != null ? _a : true,
1488
1553
  useDescribe: (_b = options.useDescribe) != null ? _b : false,
1554
+ defaultNullable: (_c = options.defaultNullable) != null ? _c : false,
1489
1555
  schemaType: options.schemaType || "all",
1490
1556
  prefix: options.prefix,
1491
1557
  suffix: options.suffix,
1492
1558
  stripSchemaPrefix: options.stripSchemaPrefix,
1493
- showStats: (_c = options.showStats) != null ? _c : true,
1559
+ stripPathPrefix: options.stripPathPrefix,
1560
+ showStats: (_d = options.showStats) != null ? _d : true,
1494
1561
  request: options.request,
1495
1562
  response: options.response,
1496
1563
  operationFilters: options.operationFilters,
1497
1564
  ignoreHeaders: options.ignoreHeaders,
1498
- cacheSize: (_d = options.cacheSize) != null ? _d : 1e3,
1499
- batchSize: (_e = options.batchSize) != null ? _e : 10
1565
+ cacheSize: (_e = options.cacheSize) != null ? _e : 1e3,
1566
+ batchSize: (_f = options.batchSize) != null ? _f : 10,
1567
+ customDateTimeFormatRegex: options.customDateTimeFormatRegex
1500
1568
  };
1501
1569
  if (this.options.cacheSize) {
1502
1570
  configurePatternCache(this.options.cacheSize);
1503
1571
  }
1572
+ if (this.options.customDateTimeFormatRegex) {
1573
+ configureDateTimeFormat(this.options.customDateTimeFormatRegex);
1574
+ }
1504
1575
  try {
1505
1576
  const fs = __require("fs");
1506
1577
  if (!fs.existsSync(this.options.input)) {
@@ -1563,6 +1634,7 @@ var OpenApiGenerator = class {
1563
1634
  mode: this.requestOptions.mode,
1564
1635
  includeDescriptions: this.requestOptions.includeDescriptions,
1565
1636
  useDescribe: this.requestOptions.useDescribe,
1637
+ defaultNullable: (_g = this.options.defaultNullable) != null ? _g : false,
1566
1638
  namingOptions: {
1567
1639
  prefix: this.options.prefix,
1568
1640
  suffix: this.options.suffix
@@ -1580,6 +1652,9 @@ var OpenApiGenerator = class {
1580
1652
  throw new SpecValidationError("No schemas found in OpenAPI spec", { filePath: this.options.input });
1581
1653
  }
1582
1654
  for (const [name, schema] of Object.entries(this.spec.components.schemas)) {
1655
+ if (this.options.operationFilters && this.schemaUsageMap.size > 0 && !this.schemaUsageMap.has(name)) {
1656
+ continue;
1657
+ }
1583
1658
  this.generateComponentSchema(name, schema);
1584
1659
  }
1585
1660
  this.generateQueryParameterSchemas();
@@ -1913,7 +1988,7 @@ var OpenApiGenerator = class {
1913
1988
  * Generate schema for a component
1914
1989
  */
1915
1990
  generateComponentSchema(name, schema) {
1916
- var _a, _b;
1991
+ var _a, _b, _c;
1917
1992
  if (!this.schemaDependencies.has(name)) {
1918
1993
  this.schemaDependencies.set(name, /* @__PURE__ */ new Set());
1919
1994
  }
@@ -1945,14 +2020,14 @@ ${typeCode}`;
1945
2020
  mode: resolvedOptions.mode,
1946
2021
  includeDescriptions: resolvedOptions.includeDescriptions,
1947
2022
  useDescribe: resolvedOptions.useDescribe,
2023
+ defaultNullable: (_b = this.options.defaultNullable) != null ? _b : false,
1948
2024
  namingOptions: {
1949
2025
  prefix: this.options.prefix,
1950
2026
  suffix: this.options.suffix
1951
2027
  },
1952
2028
  stripSchemaPrefix: this.options.stripSchemaPrefix
1953
2029
  });
1954
- const isAlias = !!(schema.$ref && !schema.properties && !schema.allOf && !schema.oneOf && !schema.anyOf);
1955
- const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, isAlias);
2030
+ const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, true);
1956
2031
  const zodSchemaCode = `${jsdoc}export const ${schemaName} = ${zodSchema};`;
1957
2032
  if (zodSchema.includes("z.discriminatedUnion(")) {
1958
2033
  const match = zodSchema.match(/z\.discriminatedUnion\([^,]+,\s*\[([^\]]+)\]/);
@@ -1962,7 +2037,7 @@ ${typeCode}`;
1962
2037
  const depMatch = ref.match(/^([a-z][a-zA-Z0-9]*?)Schema$/);
1963
2038
  if (depMatch) {
1964
2039
  const depName = depMatch[1].charAt(0).toUpperCase() + depMatch[1].slice(1);
1965
- (_b = this.schemaDependencies.get(name)) == null ? void 0 : _b.add(depName);
2040
+ (_c = this.schemaDependencies.get(name)) == null ? void 0 : _c.add(depName);
1966
2041
  }
1967
2042
  }
1968
2043
  }
@@ -1986,7 +2061,7 @@ ${typeCode}`;
1986
2061
  if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
1987
2062
  continue;
1988
2063
  }
1989
- if (!operation.operationId || !operation.parameters || !Array.isArray(operation.parameters)) {
2064
+ if (!operation.parameters || !Array.isArray(operation.parameters)) {
1990
2065
  continue;
1991
2066
  }
1992
2067
  const queryParams = operation.parameters.filter(
@@ -1995,7 +2070,13 @@ ${typeCode}`;
1995
2070
  if (queryParams.length === 0) {
1996
2071
  continue;
1997
2072
  }
1998
- const pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
2073
+ let pascalOperationId;
2074
+ if (operation.operationId) {
2075
+ pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
2076
+ } else {
2077
+ const strippedPath = stripPathPrefix(path, this.options.stripPathPrefix);
2078
+ pascalOperationId = this.generateMethodNameFromPath(method, strippedPath);
2079
+ }
1999
2080
  const schemaName = `${pascalOperationId}QueryParams`;
2000
2081
  if (!this.schemaDependencies.has(schemaName)) {
2001
2082
  this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
@@ -2043,8 +2124,9 @@ ${propsCode}
2043
2124
  const prefixedName = this.options.prefix ? `${toPascalCase(this.options.prefix)}${operationName}` : operationName;
2044
2125
  const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
2045
2126
  const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}QueryParamsSchema`;
2127
+ const jsdocOperationName = operation.operationId || `${method.toUpperCase()} ${path}`;
2046
2128
  const jsdoc = `/**
2047
- * Query parameters for ${operation.operationId}
2129
+ * Query parameters for ${jsdocOperationName}
2048
2130
  */
2049
2131
  `;
2050
2132
  const fullSchemaCode = `${jsdoc}export const ${camelCaseSchemaName} = ${schemaCode};`;
@@ -2053,6 +2135,35 @@ ${propsCode}
2053
2135
  }
2054
2136
  }
2055
2137
  }
2138
+ /**
2139
+ * Generate a PascalCase method name from HTTP method and path
2140
+ * Used as fallback when operationId is not available
2141
+ * @internal
2142
+ */
2143
+ generateMethodNameFromPath(method, path) {
2144
+ const segments = path.split("/").filter(Boolean).map((segment) => {
2145
+ if (segment.startsWith("{") && segment.endsWith("}")) {
2146
+ const paramName = segment.slice(1, -1);
2147
+ return `By${this.capitalizeSegment(paramName)}`;
2148
+ }
2149
+ return this.capitalizeSegment(segment);
2150
+ }).join("");
2151
+ const capitalizedMethod = method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();
2152
+ return `${capitalizedMethod}${segments}`;
2153
+ }
2154
+ /**
2155
+ * Capitalizes a path segment, handling special characters like dashes, underscores, and dots
2156
+ * @internal
2157
+ */
2158
+ capitalizeSegment(str) {
2159
+ if (str.includes("-") || str.includes("_") || str.includes(".")) {
2160
+ return str.split(/[-_.]/).map((part) => {
2161
+ if (!part) return "";
2162
+ return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
2163
+ }).join("");
2164
+ }
2165
+ return str.charAt(0).toUpperCase() + str.slice(1);
2166
+ }
2056
2167
  /**
2057
2168
  * Check if a header should be ignored based on filter patterns
2058
2169
  * @internal
@@ -2068,7 +2179,7 @@ ${propsCode}
2068
2179
  const headerLower = headerName.toLowerCase();
2069
2180
  return ignorePatterns.some((pattern) => {
2070
2181
  const patternLower = pattern.toLowerCase();
2071
- return minimatch2(headerLower, patternLower);
2182
+ return minimatch3(headerLower, patternLower);
2072
2183
  });
2073
2184
  }
2074
2185
  /**
@@ -2089,7 +2200,7 @@ ${propsCode}
2089
2200
  if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
2090
2201
  continue;
2091
2202
  }
2092
- if (!operation.operationId || !operation.parameters || !Array.isArray(operation.parameters)) {
2203
+ if (!operation.parameters || !Array.isArray(operation.parameters)) {
2093
2204
  continue;
2094
2205
  }
2095
2206
  const headerParams = operation.parameters.filter(
@@ -2098,7 +2209,13 @@ ${propsCode}
2098
2209
  if (headerParams.length === 0) {
2099
2210
  continue;
2100
2211
  }
2101
- const pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
2212
+ let pascalOperationId;
2213
+ if (operation.operationId) {
2214
+ pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
2215
+ } else {
2216
+ const strippedPath = stripPathPrefix(path, this.options.stripPathPrefix);
2217
+ pascalOperationId = this.generateMethodNameFromPath(method, strippedPath);
2218
+ }
2102
2219
  const schemaName = `${pascalOperationId}HeaderParams`;
2103
2220
  if (!this.schemaDependencies.has(schemaName)) {
2104
2221
  this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
@@ -2135,8 +2252,9 @@ ${propsCode}
2135
2252
  const prefixedName = this.options.prefix ? `${toPascalCase(this.options.prefix)}${operationName}` : operationName;
2136
2253
  const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
2137
2254
  const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}HeaderParamsSchema`;
2255
+ const jsdocOperationName = operation.operationId || `${method.toUpperCase()} ${path}`;
2138
2256
  const jsdoc = `/**
2139
- * Header parameters for ${operation.operationId}
2257
+ * Header parameters for ${jsdocOperationName}
2140
2258
  */
2141
2259
  `;
2142
2260
  const fullSchemaCode = `${jsdoc}export const ${camelCaseSchemaName} = ${schemaCode};`;
@@ -2156,8 +2274,22 @@ ${propsCode}
2156
2274
  return `${schemaName}Schema`;
2157
2275
  }
2158
2276
  if (schema.enum) {
2159
- const enumValues = schema.enum.map((v) => typeof v === "string" ? `"${v}"` : v).join(", ");
2160
- return `z.enum([${enumValues}])`;
2277
+ const allBooleans = schema.enum.every((v) => typeof v === "boolean");
2278
+ if (allBooleans) {
2279
+ return "z.boolean()";
2280
+ }
2281
+ const allStrings = schema.enum.every((v) => typeof v === "string");
2282
+ if (allStrings) {
2283
+ const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
2284
+ return `z.enum([${enumValues}])`;
2285
+ }
2286
+ const literalValues = schema.enum.map((v) => {
2287
+ if (typeof v === "string") {
2288
+ return `z.literal("${v}")`;
2289
+ }
2290
+ return `z.literal(${v})`;
2291
+ }).join(", ");
2292
+ return `z.union([${literalValues}])`;
2161
2293
  }
2162
2294
  const type = schema.type;
2163
2295
  if (type === "string") {