@cerios/openapi-to-zod 0.1.2 → 0.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
@@ -243,16 +243,6 @@ interface OpenAPISpec {
243
243
  * - 'sequential': Process specs one at a time (safer for resource constraints)
244
244
  */
245
245
  type ExecutionMode = "parallel" | "sequential";
246
- /**
247
- * Configuration for a single OpenAPI spec
248
- * Extends GeneratorOptions with all the same properties
249
- */
250
- interface SpecConfig extends GeneratorOptions {
251
- /**
252
- * Optional name/identifier for this spec (for logging purposes)
253
- */
254
- name?: string;
255
- }
256
246
  /**
257
247
  * Root configuration file structure
258
248
  */
@@ -266,7 +256,7 @@ interface ConfigFile {
266
256
  * Array of OpenAPI specifications to process
267
257
  * Each spec must have input and output paths
268
258
  */
269
- specs: SpecConfig[];
259
+ specs: GeneratorOptions[];
270
260
  /**
271
261
  * Execution mode for batch processing
272
262
  * @default "parallel"
@@ -371,6 +361,14 @@ declare class ZodSchemaGenerator {
371
361
  * Generate schema for a component
372
362
  */
373
363
  private generateComponentSchema;
364
+ /**
365
+ * Generate query parameter schemas for each operation
366
+ */
367
+ private generateQueryParameterSchemas;
368
+ /**
369
+ * Generate Zod type for a query parameter schema
370
+ */
371
+ private generateQueryParamType;
374
372
  /**
375
373
  * Generate native TypeScript enum
376
374
  */
@@ -402,4 +400,4 @@ declare class ZodSchemaGenerator {
402
400
  private generateStats;
403
401
  }
404
402
 
405
- export { CircularReferenceError, CliOptionsError, type ConfigFile, ConfigValidationError, type ExecutionMode, FileOperationError, GeneratorError, type GeneratorOptions, type OpenAPISpec, SchemaGenerationError, type SpecConfig, SpecValidationError, ZodSchemaGenerator, defineConfig };
403
+ export { CircularReferenceError, CliOptionsError, type ConfigFile, ConfigValidationError, type ExecutionMode, FileOperationError, GeneratorError, type GeneratorOptions, type OpenAPISpec, SchemaGenerationError, SpecValidationError, ZodSchemaGenerator, defineConfig };
package/dist/index.d.ts CHANGED
@@ -243,16 +243,6 @@ interface OpenAPISpec {
243
243
  * - 'sequential': Process specs one at a time (safer for resource constraints)
244
244
  */
245
245
  type ExecutionMode = "parallel" | "sequential";
246
- /**
247
- * Configuration for a single OpenAPI spec
248
- * Extends GeneratorOptions with all the same properties
249
- */
250
- interface SpecConfig extends GeneratorOptions {
251
- /**
252
- * Optional name/identifier for this spec (for logging purposes)
253
- */
254
- name?: string;
255
- }
256
246
  /**
257
247
  * Root configuration file structure
258
248
  */
@@ -266,7 +256,7 @@ interface ConfigFile {
266
256
  * Array of OpenAPI specifications to process
267
257
  * Each spec must have input and output paths
268
258
  */
269
- specs: SpecConfig[];
259
+ specs: GeneratorOptions[];
270
260
  /**
271
261
  * Execution mode for batch processing
272
262
  * @default "parallel"
@@ -371,6 +361,14 @@ declare class ZodSchemaGenerator {
371
361
  * Generate schema for a component
372
362
  */
373
363
  private generateComponentSchema;
364
+ /**
365
+ * Generate query parameter schemas for each operation
366
+ */
367
+ private generateQueryParameterSchemas;
368
+ /**
369
+ * Generate Zod type for a query parameter schema
370
+ */
371
+ private generateQueryParamType;
374
372
  /**
375
373
  * Generate native TypeScript enum
376
374
  */
@@ -402,4 +400,4 @@ declare class ZodSchemaGenerator {
402
400
  private generateStats;
403
401
  }
404
402
 
405
- export { CircularReferenceError, CliOptionsError, type ConfigFile, ConfigValidationError, type ExecutionMode, FileOperationError, GeneratorError, type GeneratorOptions, type OpenAPISpec, SchemaGenerationError, type SpecConfig, SpecValidationError, ZodSchemaGenerator, defineConfig };
403
+ export { CircularReferenceError, CliOptionsError, type ConfigFile, ConfigValidationError, type ExecutionMode, FileOperationError, GeneratorError, type GeneratorOptions, type OpenAPISpec, SchemaGenerationError, SpecValidationError, ZodSchemaGenerator, defineConfig };
package/dist/index.js CHANGED
@@ -147,7 +147,7 @@ function generateEnum(name, values, options) {
147
147
  const enumCode = `export enum ${enumName} {
148
148
  ${enumEntries}
149
149
  }`;
150
- const schemaCode2 = `export const ${schemaName} = z.enum(${enumName});`;
150
+ const schemaCode2 = `export const ${schemaName} = z.nativeEnum(${enumName});`;
151
151
  const typeCode2 = `export type ${name} = z.infer<typeof ${schemaName}>;`;
152
152
  return { enumCode, schemaCode: schemaCode2, typeCode: typeCode2 };
153
153
  }
@@ -330,20 +330,20 @@ function generateArrayValidation(schema, context) {
330
330
  }
331
331
 
332
332
  // src/validators/composition-validator.ts
333
- function generateUnion(schemas, discriminator, isNullable2, context, options) {
333
+ function generateUnion(schemas, discriminator, isNullable2, context, options, currentSchema) {
334
334
  if (discriminator) {
335
335
  let resolvedSchemas = schemas;
336
336
  if ((options == null ? void 0 : options.discriminatorMapping) && context.resolveDiscriminatorMapping) {
337
337
  resolvedSchemas = context.resolveDiscriminatorMapping(options.discriminatorMapping, schemas);
338
338
  }
339
- let schemaStrings2 = resolvedSchemas.map((s) => context.generatePropertySchema(s));
339
+ let schemaStrings2 = resolvedSchemas.map((s) => context.generatePropertySchema(s, currentSchema));
340
340
  if (options == null ? void 0 : options.passthrough) {
341
341
  schemaStrings2 = schemaStrings2.map((s) => s.includes(".catchall(") ? s : `${s}.catchall(z.unknown())`);
342
342
  }
343
343
  const union2 = `z.discriminatedUnion("${discriminator}", [${schemaStrings2.join(", ")}])`;
344
344
  return wrapNullable(union2, isNullable2);
345
345
  }
346
- let schemaStrings = schemas.map((s) => context.generatePropertySchema(s));
346
+ let schemaStrings = schemas.map((s) => context.generatePropertySchema(s, currentSchema));
347
347
  if (options == null ? void 0 : options.passthrough) {
348
348
  schemaStrings = schemaStrings.map((s) => s.includes(".catchall(") ? s : `${s}.catchall(z.unknown())`);
349
349
  }
@@ -1176,7 +1176,8 @@ var _PropertyGenerator = class _PropertyGenerator {
1176
1176
  {
1177
1177
  passthrough: needsPassthrough,
1178
1178
  discriminatorMapping: (_c = schema.discriminator) == null ? void 0 : _c.mapping
1179
- }
1179
+ },
1180
+ currentSchema
1180
1181
  );
1181
1182
  if (schema.unevaluatedProperties !== void 0) {
1182
1183
  composition = this.applyUnevaluatedProperties(composition, schema);
@@ -1196,7 +1197,8 @@ var _PropertyGenerator = class _PropertyGenerator {
1196
1197
  {
1197
1198
  passthrough: needsPassthrough,
1198
1199
  discriminatorMapping: (_e = schema.discriminator) == null ? void 0 : _e.mapping
1199
- }
1200
+ },
1201
+ currentSchema
1200
1202
  );
1201
1203
  if (schema.unevaluatedProperties !== void 0) {
1202
1204
  composition = this.applyUnevaluatedProperties(composition, schema);
@@ -1367,10 +1369,13 @@ var ZodSchemaGenerator = class {
1367
1369
  }
1368
1370
  for (const [name, schema] of Object.entries(this.spec.components.schemas)) {
1369
1371
  if (schema.enum) {
1370
- const typeMode = this.schemaTypeModeMap.get(name) || "inferred";
1371
- if (typeMode === "inferred") {
1372
+ const context = this.schemaUsageMap.get(name);
1373
+ const resolvedOptions = context === "response" ? this.responseOptions : this.requestOptions;
1374
+ if (resolvedOptions.enumType === "typescript") {
1375
+ this.generateNativeEnum(name, schema);
1376
+ } else {
1372
1377
  const { enumCode } = generateEnum(name, schema.enum, {
1373
- enumType: this.options.enumType || "zod",
1378
+ enumType: "zod",
1374
1379
  prefix: this.options.prefix,
1375
1380
  suffix: this.options.suffix
1376
1381
  });
@@ -1378,14 +1383,13 @@ var ZodSchemaGenerator = class {
1378
1383
  this.enums.set(name, enumCode);
1379
1384
  this.needsZodImport = true;
1380
1385
  }
1381
- } else {
1382
- this.generateNativeEnum(name, schema);
1383
1386
  }
1384
1387
  }
1385
1388
  }
1386
1389
  for (const [name, schema] of Object.entries(this.spec.components.schemas)) {
1387
1390
  this.generateComponentSchema(name, schema);
1388
1391
  }
1392
+ this.generateQueryParameterSchemas();
1389
1393
  const orderedSchemaNames = this.topologicalSort();
1390
1394
  const output = ["// Auto-generated by @cerios/openapi-to-zod", "// Do not edit this file manually", ""];
1391
1395
  if (this.options.showStats === true) {
@@ -1396,12 +1400,8 @@ var ZodSchemaGenerator = class {
1396
1400
  output.push('import { z } from "zod";');
1397
1401
  output.push("");
1398
1402
  }
1399
- if (this.enums.size > 0 || this.nativeEnums.size > 0) {
1400
- output.push("// Enums");
1401
- for (const enumCode of this.enums.values()) {
1402
- output.push(enumCode);
1403
- output.push("");
1404
- }
1403
+ if (this.nativeEnums.size > 0) {
1404
+ output.push("// Native Enums");
1405
1405
  for (const enumCode of this.nativeEnums.values()) {
1406
1406
  output.push(enumCode);
1407
1407
  output.push("");
@@ -1409,9 +1409,13 @@ var ZodSchemaGenerator = class {
1409
1409
  }
1410
1410
  output.push("// Schemas and Types");
1411
1411
  for (const name of orderedSchemaNames) {
1412
+ const enumCode = this.enums.get(name);
1412
1413
  const schemaCode = this.schemas.get(name);
1413
1414
  const typeCode = this.types.get(name);
1414
- if (schemaCode) {
1415
+ if (enumCode) {
1416
+ output.push(enumCode);
1417
+ output.push("");
1418
+ } else if (schemaCode) {
1415
1419
  output.push(schemaCode);
1416
1420
  if (!schemaCode.includes(`export type ${name}`)) {
1417
1421
  const schemaName = `${toCamelCase(name, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
@@ -1749,10 +1753,19 @@ var ZodSchemaGenerator = class {
1749
1753
  const context = this.schemaUsageMap.get(name);
1750
1754
  const resolvedOptions = context === "response" ? this.responseOptions : this.requestOptions;
1751
1755
  if (schema.enum) {
1752
- if (typeMode === "inferred") {
1753
- const jsdoc = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
1756
+ const jsdoc = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
1757
+ if (resolvedOptions.enumType === "typescript") {
1758
+ const { schemaCode, typeCode } = generateEnum(name, schema.enum, {
1759
+ enumType: "typescript",
1760
+ prefix: this.options.prefix,
1761
+ suffix: this.options.suffix
1762
+ });
1763
+ const enumSchemaCode = `${jsdoc}${schemaCode}
1764
+ ${typeCode}`;
1765
+ this.schemas.set(name, enumSchemaCode);
1766
+ } else {
1754
1767
  const { enumCode, schemaCode, typeCode } = generateEnum(name, schema.enum, {
1755
- enumType: resolvedOptions.enumType,
1768
+ enumType: "zod",
1756
1769
  prefix: this.options.prefix,
1757
1770
  suffix: this.options.suffix
1758
1771
  });
@@ -1811,6 +1824,133 @@ ${typeCode}`;
1811
1824
  this.schemas.set(name, zodSchemaCode);
1812
1825
  }
1813
1826
  }
1827
+ /**
1828
+ * Generate query parameter schemas for each operation
1829
+ */
1830
+ generateQueryParameterSchemas() {
1831
+ var _a;
1832
+ if (!this.spec.paths) {
1833
+ return;
1834
+ }
1835
+ for (const [_path, pathItem] of Object.entries(this.spec.paths)) {
1836
+ if (!pathItem || typeof pathItem !== "object") continue;
1837
+ const methods = ["get", "post", "put", "patch", "delete", "head", "options"];
1838
+ for (const method of methods) {
1839
+ const operation = pathItem[method];
1840
+ if (!operation) continue;
1841
+ if (!operation.operationId || !operation.parameters || !Array.isArray(operation.parameters)) {
1842
+ continue;
1843
+ }
1844
+ const queryParams = operation.parameters.filter(
1845
+ (param) => param && typeof param === "object" && param.in === "query"
1846
+ );
1847
+ if (queryParams.length === 0) {
1848
+ continue;
1849
+ }
1850
+ const pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
1851
+ const schemaName = `${pascalOperationId}QueryParams`;
1852
+ if (!this.schemaDependencies.has(schemaName)) {
1853
+ this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
1854
+ }
1855
+ const properties = {};
1856
+ const required = [];
1857
+ for (const param of queryParams) {
1858
+ const paramName = param.name;
1859
+ const isRequired = param.required === true;
1860
+ const paramSchema = param.schema;
1861
+ if (!paramSchema) continue;
1862
+ let zodType = this.generateQueryParamType(paramSchema, param);
1863
+ if (paramSchema.type === "array" && paramSchema.items) {
1864
+ const itemType = this.generateQueryParamType(paramSchema.items, param);
1865
+ zodType = `z.array(${itemType})`;
1866
+ }
1867
+ if (param.description && this.requestOptions.includeDescriptions) {
1868
+ if (this.requestOptions.useDescribe) {
1869
+ zodType = `${zodType}.describe(${JSON.stringify(param.description)})`;
1870
+ }
1871
+ }
1872
+ if (!isRequired) {
1873
+ zodType = `${zodType}.optional()`;
1874
+ }
1875
+ properties[paramName] = zodType;
1876
+ if (isRequired) {
1877
+ required.push(paramName);
1878
+ }
1879
+ if (paramSchema.$ref) {
1880
+ const refName = resolveRef(paramSchema.$ref);
1881
+ (_a = this.schemaDependencies.get(schemaName)) == null ? void 0 : _a.add(refName);
1882
+ }
1883
+ }
1884
+ const objectMode = this.requestOptions.mode;
1885
+ const zodMethod = objectMode === "strict" ? "strictObject" : objectMode === "loose" ? "looseObject" : "object";
1886
+ const propsCode = Object.entries(properties).map(([key, value]) => {
1887
+ const needsQuotes = !/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key);
1888
+ const quotedKey = needsQuotes ? `"${key}"` : key;
1889
+ return ` ${quotedKey}: ${value}`;
1890
+ }).join(",\n");
1891
+ const schemaCode = `z.${zodMethod}({
1892
+ ${propsCode}
1893
+ })`;
1894
+ const operationName = pascalOperationId;
1895
+ const prefixedName = this.options.prefix ? `${toPascalCase(this.options.prefix)}${operationName}` : operationName;
1896
+ const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
1897
+ const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}QueryParamsSchema`;
1898
+ const jsdoc = `/**
1899
+ * Query parameters for ${operation.operationId}
1900
+ */
1901
+ `;
1902
+ const fullSchemaCode = `${jsdoc}export const ${camelCaseSchemaName} = ${schemaCode};`;
1903
+ this.schemas.set(schemaName, fullSchemaCode);
1904
+ this.needsZodImport = true;
1905
+ }
1906
+ }
1907
+ }
1908
+ /**
1909
+ * Generate Zod type for a query parameter schema
1910
+ */
1911
+ generateQueryParamType(schema, param) {
1912
+ if (schema.$ref) {
1913
+ const refName = resolveRef(schema.$ref);
1914
+ const schemaName = toCamelCase(refName, { prefix: this.options.prefix, suffix: this.options.suffix });
1915
+ return `${schemaName}Schema`;
1916
+ }
1917
+ if (schema.enum) {
1918
+ const enumValues = schema.enum.map((v) => typeof v === "string" ? `"${v}"` : v).join(", ");
1919
+ return `z.enum([${enumValues}])`;
1920
+ }
1921
+ const type = schema.type;
1922
+ if (type === "string") {
1923
+ let zodType = "z.string()";
1924
+ if (schema.minLength !== void 0) zodType = `${zodType}.min(${schema.minLength})`;
1925
+ if (schema.maxLength !== void 0) zodType = `${zodType}.max(${schema.maxLength})`;
1926
+ if (schema.pattern) zodType = `${zodType}.regex(/${schema.pattern}/)`;
1927
+ if (schema.format === "email") zodType = `${zodType}.email()`;
1928
+ if (schema.format === "uri" || schema.format === "url") zodType = `${zodType}.url()`;
1929
+ if (schema.format === "uuid") zodType = `${zodType}.uuid()`;
1930
+ return zodType;
1931
+ }
1932
+ if (type === "number" || type === "integer") {
1933
+ let zodType = type === "integer" ? "z.number().int()" : "z.number()";
1934
+ if (schema.minimum !== void 0) {
1935
+ zodType = schema.exclusiveMinimum ? `${zodType}.gt(${schema.minimum})` : `${zodType}.gte(${schema.minimum})`;
1936
+ }
1937
+ if (schema.maximum !== void 0) {
1938
+ zodType = schema.exclusiveMaximum ? `${zodType}.lt(${schema.maximum})` : `${zodType}.lte(${schema.maximum})`;
1939
+ }
1940
+ return zodType;
1941
+ }
1942
+ if (type === "boolean") {
1943
+ return "z.boolean()";
1944
+ }
1945
+ if (type === "array" && schema.items) {
1946
+ const itemType = this.generateQueryParamType(schema.items, param);
1947
+ let arrayType = `z.array(${itemType})`;
1948
+ if (schema.minItems !== void 0) arrayType = `${arrayType}.min(${schema.minItems})`;
1949
+ if (schema.maxItems !== void 0) arrayType = `${arrayType}.max(${schema.maxItems})`;
1950
+ return arrayType;
1951
+ }
1952
+ return "z.unknown()";
1953
+ }
1814
1954
  /**
1815
1955
  * Generate native TypeScript enum
1816
1956
  */
@@ -1821,8 +1961,8 @@ ${typeCode}`;
1821
1961
  const jsdoc = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
1822
1962
  if (resolvedOptions.nativeEnumType === "enum") {
1823
1963
  const enumName = `${name}Enum`;
1824
- const members = schema.enum.map((value, index) => {
1825
- const key = typeof value === "string" ? this.toEnumKey(value) : `Value${index}`;
1964
+ const members = schema.enum.map((value) => {
1965
+ const key = typeof value === "string" ? this.toEnumKey(value) : `N${value}`;
1826
1966
  const val = typeof value === "string" ? `"${value}"` : value;
1827
1967
  return ` ${key} = ${val}`;
1828
1968
  }).join(",\n");
@@ -1965,7 +2105,11 @@ ${props.join("\n")}
1965
2105
  const visited = /* @__PURE__ */ new Set();
1966
2106
  const visiting = /* @__PURE__ */ new Set();
1967
2107
  const aliases = [];
2108
+ const circularDeps = /* @__PURE__ */ new Set();
1968
2109
  const codeCache = /* @__PURE__ */ new Map();
2110
+ for (const [name, code] of this.enums) {
2111
+ codeCache.set(name, code);
2112
+ }
1969
2113
  for (const [name, code] of this.schemas) {
1970
2114
  codeCache.set(name, code);
1971
2115
  }
@@ -1975,6 +2119,7 @@ ${props.join("\n")}
1975
2119
  const visit = (name) => {
1976
2120
  if (visited.has(name)) return;
1977
2121
  if (visiting.has(name)) {
2122
+ circularDeps.add(name);
1978
2123
  return;
1979
2124
  }
1980
2125
  visiting.add(name);
@@ -1989,19 +2134,27 @@ ${props.join("\n")}
1989
2134
  const deps = this.schemaDependencies.get(name);
1990
2135
  if (deps && deps.size > 0) {
1991
2136
  for (const dep of deps) {
1992
- if (this.schemas.has(dep) || this.types.has(dep)) {
2137
+ if (this.enums.has(dep) || this.schemas.has(dep) || this.types.has(dep)) {
1993
2138
  visit(dep);
1994
2139
  }
1995
2140
  }
1996
2141
  }
1997
2142
  visiting.delete(name);
1998
2143
  visited.add(name);
1999
- sorted.push(name);
2144
+ if (!circularDeps.has(name)) {
2145
+ sorted.push(name);
2146
+ }
2000
2147
  };
2001
- const allNames = /* @__PURE__ */ new Set([...this.schemas.keys(), ...this.types.keys()]);
2148
+ const allNames = /* @__PURE__ */ new Set([...this.enums.keys(), ...this.schemas.keys(), ...this.types.keys()]);
2002
2149
  for (const name of allNames) {
2003
2150
  visit(name);
2004
2151
  }
2152
+ for (const name of circularDeps) {
2153
+ if (!visited.has(name)) {
2154
+ sorted.push(name);
2155
+ visited.add(name);
2156
+ }
2157
+ }
2005
2158
  return [...sorted, ...aliases];
2006
2159
  }
2007
2160
  /**