@cerios/openapi-to-zod 1.1.1 → 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-CI48CjiU.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-CI48CjiU.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-CI48CjiU.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-CI48CjiU.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
@@ -190,14 +190,17 @@ function escapeJSDoc(str) {
190
190
  function wrapNullable(validation, isNullable2) {
191
191
  return isNullable2 ? `${validation}.nullable()` : validation;
192
192
  }
193
- function isNullable(schema) {
193
+ function isNullable(schema, defaultNullable = false) {
194
194
  if (schema.nullable === true) {
195
195
  return true;
196
196
  }
197
+ if (schema.nullable === false) {
198
+ return false;
199
+ }
197
200
  if (Array.isArray(schema.type)) {
198
201
  return schema.type.includes("null");
199
202
  }
200
- return false;
203
+ return defaultNullable;
201
204
  }
202
205
  function getPrimaryType(schema) {
203
206
  if (Array.isArray(schema.type)) {
@@ -363,6 +366,22 @@ function stripPrefix(input, pattern, ensureLeadingChar) {
363
366
  }
364
367
  return input;
365
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
+ }
366
385
 
367
386
  // src/validators/array-validator.ts
368
387
  function generateArrayValidation(schema, context) {
@@ -888,7 +907,7 @@ function configurePatternCache(size) {
888
907
  PATTERN_CACHE = new LRUCache(size);
889
908
  }
890
909
  }
891
- var FORMAT_MAP = {
910
+ var DEFAULT_FORMAT_MAP = {
892
911
  uuid: "z.uuid()",
893
912
  email: "z.email()",
894
913
  uri: "z.url()",
@@ -898,7 +917,6 @@ var FORMAT_MAP = {
898
917
  byte: "z.base64()",
899
918
  binary: "z.string()",
900
919
  date: "z.iso.date()",
901
- "date-time": "z.iso.datetime()",
902
920
  time: "z.iso.time()",
903
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" })',
904
922
  ipv4: "z.ipv4()",
@@ -917,6 +935,30 @@ var FORMAT_MAP = {
917
935
  "json-pointer": 'z.string().refine((val) => val === "" || /^(\\/([^~/]|~0|~1)+)+$/.test(val), { message: "Must be a valid JSON Pointer (RFC 6901)" })',
918
936
  "relative-json-pointer": 'z.string().refine((val) => /^(0|[1-9]\\d*)(#|(\\/([^~/]|~0|~1)+)*)$/.test(val), { message: "Must be a valid relative JSON Pointer" })'
919
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
+ }
920
962
  function generateStringValidation(schema, useDescribe) {
921
963
  let validation = FORMAT_MAP[schema.format || ""] || "z.string()";
922
964
  if (schema.minLength !== void 0) {
@@ -1206,7 +1248,8 @@ var _PropertyGenerator = class _PropertyGenerator {
1206
1248
  if ((this.context.schemaType === "request" || this.context.schemaType === "response") && schema.properties) {
1207
1249
  schema = this.filterNestedProperties(schema);
1208
1250
  }
1209
- const nullable = isNullable(schema);
1251
+ const effectiveDefaultNullable = isTopLevel ? false : this.context.defaultNullable;
1252
+ const nullable = isNullable(schema, effectiveDefaultNullable);
1210
1253
  if (hasMultipleTypes(schema)) {
1211
1254
  const union = this.generateMultiTypeUnion(schema, currentSchema);
1212
1255
  return wrapNullable(union, nullable);
@@ -1525,7 +1568,7 @@ var OpenApiGenerator = class {
1525
1568
  this.schemaUsageMap = /* @__PURE__ */ new Map();
1526
1569
  this.needsZodImport = true;
1527
1570
  this.filterStats = createFilterStatistics();
1528
- var _a, _b, _c, _d, _e;
1571
+ var _a, _b, _c, _d, _e, _f, _g;
1529
1572
  if (!options.input) {
1530
1573
  throw new ConfigurationError("Input path is required", { providedOptions: options });
1531
1574
  }
@@ -1535,21 +1578,27 @@ var OpenApiGenerator = class {
1535
1578
  output: options.output,
1536
1579
  includeDescriptions: (_a = options.includeDescriptions) != null ? _a : true,
1537
1580
  useDescribe: (_b = options.useDescribe) != null ? _b : false,
1581
+ defaultNullable: (_c = options.defaultNullable) != null ? _c : false,
1538
1582
  schemaType: options.schemaType || "all",
1539
1583
  prefix: options.prefix,
1540
1584
  suffix: options.suffix,
1541
1585
  stripSchemaPrefix: options.stripSchemaPrefix,
1542
- showStats: (_c = options.showStats) != null ? _c : true,
1586
+ stripPathPrefix: options.stripPathPrefix,
1587
+ showStats: (_d = options.showStats) != null ? _d : true,
1543
1588
  request: options.request,
1544
1589
  response: options.response,
1545
1590
  operationFilters: options.operationFilters,
1546
1591
  ignoreHeaders: options.ignoreHeaders,
1547
- cacheSize: (_d = options.cacheSize) != null ? _d : 1e3,
1548
- 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
1549
1595
  };
1550
1596
  if (this.options.cacheSize) {
1551
1597
  configurePatternCache(this.options.cacheSize);
1552
1598
  }
1599
+ if (this.options.customDateTimeFormatRegex) {
1600
+ configureDateTimeFormat(this.options.customDateTimeFormatRegex);
1601
+ }
1553
1602
  try {
1554
1603
  const fs = require("fs");
1555
1604
  if (!fs.existsSync(this.options.input)) {
@@ -1612,6 +1661,7 @@ var OpenApiGenerator = class {
1612
1661
  mode: this.requestOptions.mode,
1613
1662
  includeDescriptions: this.requestOptions.includeDescriptions,
1614
1663
  useDescribe: this.requestOptions.useDescribe,
1664
+ defaultNullable: (_g = this.options.defaultNullable) != null ? _g : false,
1615
1665
  namingOptions: {
1616
1666
  prefix: this.options.prefix,
1617
1667
  suffix: this.options.suffix
@@ -1965,7 +2015,7 @@ var OpenApiGenerator = class {
1965
2015
  * Generate schema for a component
1966
2016
  */
1967
2017
  generateComponentSchema(name, schema) {
1968
- var _a, _b;
2018
+ var _a, _b, _c;
1969
2019
  if (!this.schemaDependencies.has(name)) {
1970
2020
  this.schemaDependencies.set(name, /* @__PURE__ */ new Set());
1971
2021
  }
@@ -1997,14 +2047,14 @@ ${typeCode}`;
1997
2047
  mode: resolvedOptions.mode,
1998
2048
  includeDescriptions: resolvedOptions.includeDescriptions,
1999
2049
  useDescribe: resolvedOptions.useDescribe,
2050
+ defaultNullable: (_b = this.options.defaultNullable) != null ? _b : false,
2000
2051
  namingOptions: {
2001
2052
  prefix: this.options.prefix,
2002
2053
  suffix: this.options.suffix
2003
2054
  },
2004
2055
  stripSchemaPrefix: this.options.stripSchemaPrefix
2005
2056
  });
2006
- const isAlias = !!(schema.$ref && !schema.properties && !schema.allOf && !schema.oneOf && !schema.anyOf);
2007
- const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, isAlias);
2057
+ const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, true);
2008
2058
  const zodSchemaCode = `${jsdoc}export const ${schemaName} = ${zodSchema};`;
2009
2059
  if (zodSchema.includes("z.discriminatedUnion(")) {
2010
2060
  const match = zodSchema.match(/z\.discriminatedUnion\([^,]+,\s*\[([^\]]+)\]/);
@@ -2014,7 +2064,7 @@ ${typeCode}`;
2014
2064
  const depMatch = ref.match(/^([a-z][a-zA-Z0-9]*?)Schema$/);
2015
2065
  if (depMatch) {
2016
2066
  const depName = depMatch[1].charAt(0).toUpperCase() + depMatch[1].slice(1);
2017
- (_b = this.schemaDependencies.get(name)) == null ? void 0 : _b.add(depName);
2067
+ (_c = this.schemaDependencies.get(name)) == null ? void 0 : _c.add(depName);
2018
2068
  }
2019
2069
  }
2020
2070
  }
@@ -2038,7 +2088,7 @@ ${typeCode}`;
2038
2088
  if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
2039
2089
  continue;
2040
2090
  }
2041
- if (!operation.operationId || !operation.parameters || !Array.isArray(operation.parameters)) {
2091
+ if (!operation.parameters || !Array.isArray(operation.parameters)) {
2042
2092
  continue;
2043
2093
  }
2044
2094
  const queryParams = operation.parameters.filter(
@@ -2047,7 +2097,13 @@ ${typeCode}`;
2047
2097
  if (queryParams.length === 0) {
2048
2098
  continue;
2049
2099
  }
2050
- 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
+ }
2051
2107
  const schemaName = `${pascalOperationId}QueryParams`;
2052
2108
  if (!this.schemaDependencies.has(schemaName)) {
2053
2109
  this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
@@ -2095,8 +2151,9 @@ ${propsCode}
2095
2151
  const prefixedName = this.options.prefix ? `${toPascalCase(this.options.prefix)}${operationName}` : operationName;
2096
2152
  const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
2097
2153
  const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}QueryParamsSchema`;
2154
+ const jsdocOperationName = operation.operationId || `${method.toUpperCase()} ${path}`;
2098
2155
  const jsdoc = `/**
2099
- * Query parameters for ${operation.operationId}
2156
+ * Query parameters for ${jsdocOperationName}
2100
2157
  */
2101
2158
  `;
2102
2159
  const fullSchemaCode = `${jsdoc}export const ${camelCaseSchemaName} = ${schemaCode};`;
@@ -2105,6 +2162,35 @@ ${propsCode}
2105
2162
  }
2106
2163
  }
2107
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
+ }
2108
2194
  /**
2109
2195
  * Check if a header should be ignored based on filter patterns
2110
2196
  * @internal
@@ -2141,7 +2227,7 @@ ${propsCode}
2141
2227
  if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
2142
2228
  continue;
2143
2229
  }
2144
- if (!operation.operationId || !operation.parameters || !Array.isArray(operation.parameters)) {
2230
+ if (!operation.parameters || !Array.isArray(operation.parameters)) {
2145
2231
  continue;
2146
2232
  }
2147
2233
  const headerParams = operation.parameters.filter(
@@ -2150,7 +2236,13 @@ ${propsCode}
2150
2236
  if (headerParams.length === 0) {
2151
2237
  continue;
2152
2238
  }
2153
- 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
+ }
2154
2246
  const schemaName = `${pascalOperationId}HeaderParams`;
2155
2247
  if (!this.schemaDependencies.has(schemaName)) {
2156
2248
  this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
@@ -2187,8 +2279,9 @@ ${propsCode}
2187
2279
  const prefixedName = this.options.prefix ? `${toPascalCase(this.options.prefix)}${operationName}` : operationName;
2188
2280
  const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
2189
2281
  const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}HeaderParamsSchema`;
2282
+ const jsdocOperationName = operation.operationId || `${method.toUpperCase()} ${path}`;
2190
2283
  const jsdoc = `/**
2191
- * Header parameters for ${operation.operationId}
2284
+ * Header parameters for ${jsdocOperationName}
2192
2285
  */
2193
2286
  `;
2194
2287
  const fullSchemaCode = `${jsdoc}export const ${camelCaseSchemaName} = ${schemaCode};`;