@cerios/openapi-to-zod 0.6.0 → 1.1.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
@@ -5016,9 +5016,140 @@ var init_errors = __esm({
5016
5016
  }
5017
5017
  });
5018
5018
 
5019
+ // src/batch-executor.ts
5020
+ async function processSpec(spec, index, total, createGenerator) {
5021
+ const specInput = spec.input || "spec";
5022
+ const specOutput = spec.output || "output";
5023
+ console.log(`Processing [${index + 1}/${total}] ${specInput}...`);
5024
+ try {
5025
+ const generator = createGenerator(spec);
5026
+ generator.generate();
5027
+ return {
5028
+ spec,
5029
+ success: true
5030
+ };
5031
+ } catch (error) {
5032
+ const errorMessage = error instanceof Error ? error.message : String(error);
5033
+ console.error(`\u2717 Failed to generate ${specOutput}: ${errorMessage}`);
5034
+ return {
5035
+ spec,
5036
+ success: false,
5037
+ error: errorMessage
5038
+ };
5039
+ }
5040
+ }
5041
+ async function executeParallel(specs, createGenerator, batchSize) {
5042
+ console.log(`
5043
+ Executing ${specs.length} specification(s) in parallel (batch size: ${batchSize})...
5044
+ `);
5045
+ const results = [];
5046
+ for (let i = 0; i < specs.length; i += batchSize) {
5047
+ const batch = specs.slice(i, Math.min(i + batchSize, specs.length));
5048
+ const batchPromises = batch.map(
5049
+ (spec, batchIndex) => processSpec(spec, i + batchIndex, specs.length, createGenerator)
5050
+ );
5051
+ const batchResults = await Promise.allSettled(batchPromises);
5052
+ for (let j = 0; j < batchResults.length; j++) {
5053
+ const result = batchResults[j];
5054
+ if (result.status === "fulfilled") {
5055
+ results.push(result.value);
5056
+ } else {
5057
+ results.push({
5058
+ spec: batch[j],
5059
+ success: false,
5060
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
5061
+ });
5062
+ }
5063
+ }
5064
+ }
5065
+ return results;
5066
+ }
5067
+ async function executeSequential(specs, createGenerator) {
5068
+ console.log(`
5069
+ Executing ${specs.length} spec(s) sequentially...
5070
+ `);
5071
+ const results = [];
5072
+ for (let i = 0; i < specs.length; i++) {
5073
+ const result = await processSpec(specs[i], i, specs.length, createGenerator);
5074
+ results.push(result);
5075
+ }
5076
+ return results;
5077
+ }
5078
+ function printSummary(summary) {
5079
+ console.log(`
5080
+ ${"=".repeat(50)}`);
5081
+ console.log("Batch Execution Summary");
5082
+ console.log("=".repeat(50));
5083
+ console.log(`Total specs: ${summary.total}`);
5084
+ console.log(`Successful: ${summary.successful}`);
5085
+ console.log(`Failed: ${summary.failed}`);
5086
+ if (summary.failed > 0) {
5087
+ console.log("\nFailed specs:");
5088
+ for (const result of summary.results) {
5089
+ if (!result.success) {
5090
+ const specInput = result.spec.input || "spec";
5091
+ console.error(` \u2717 ${specInput}`);
5092
+ console.error(` Error: ${result.error}`);
5093
+ }
5094
+ }
5095
+ }
5096
+ console.log(`${"=".repeat(50)}
5097
+ `);
5098
+ }
5099
+ async function executeBatch(specs, executionMode = "parallel", createGenerator, batchSize) {
5100
+ if (specs.length === 0) {
5101
+ throw new ConfigurationError("No specs provided for batch execution", { specsCount: 0, executionMode });
5102
+ }
5103
+ let results = [];
5104
+ try {
5105
+ results = executionMode === "parallel" ? await executeParallel(specs, createGenerator, batchSize) : await executeSequential(specs, createGenerator);
5106
+ const summary = {
5107
+ total: results.length,
5108
+ successful: results.filter((r) => r.success).length,
5109
+ failed: results.filter((r) => !r.success).length,
5110
+ results
5111
+ };
5112
+ printSummary(summary);
5113
+ return summary;
5114
+ } finally {
5115
+ if (results.length > batchSize) {
5116
+ for (const result of results) {
5117
+ if (result.spec) {
5118
+ result.spec = null;
5119
+ }
5120
+ }
5121
+ if (global.gc) {
5122
+ global.gc();
5123
+ }
5124
+ }
5125
+ }
5126
+ }
5127
+ function getBatchExitCode(summary) {
5128
+ return summary.failed > 0 ? 1 : 0;
5129
+ }
5130
+ var init_batch_executor = __esm({
5131
+ "src/batch-executor.ts"() {
5132
+ "use strict";
5133
+ init_esm_shims();
5134
+ init_errors();
5135
+ }
5136
+ });
5137
+
5019
5138
  // src/utils/name-utils.ts
5139
+ function sanitizeIdentifier(str) {
5140
+ return str.replace(/[^a-zA-Z0-9._\-\s]+/g, "_");
5141
+ }
5020
5142
  function toCamelCase(str, options) {
5021
- let name = str.charAt(0).toLowerCase() + str.slice(1);
5143
+ const sanitized = sanitizeIdentifier(str);
5144
+ const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
5145
+ let name;
5146
+ if (words.length === 0) {
5147
+ name = str.charAt(0).toLowerCase() + str.slice(1);
5148
+ } else if (words.length === 1) {
5149
+ name = words[0].charAt(0).toLowerCase() + words[0].slice(1);
5150
+ } else {
5151
+ name = words[0].charAt(0).toLowerCase() + words[0].slice(1) + words.slice(1).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
5152
+ }
5022
5153
  if (options == null ? void 0 : options.prefix) {
5023
5154
  const prefix = options.prefix.toLowerCase();
5024
5155
  name = prefix + name.charAt(0).toUpperCase() + name.slice(1);
@@ -5031,12 +5162,23 @@ function toCamelCase(str, options) {
5031
5162
  }
5032
5163
  function toPascalCase(str) {
5033
5164
  const stringValue = String(str);
5034
- let result = stringValue.replace(/[^a-zA-Z0-9_]+/g, "_").split(/[-_]+/).filter((word) => word.length > 0).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
5165
+ const isAlreadyValidCase = /^[a-zA-Z][a-zA-Z0-9]*$/.test(stringValue);
5166
+ if (isAlreadyValidCase) {
5167
+ return stringValue.charAt(0).toUpperCase() + stringValue.slice(1);
5168
+ }
5169
+ const sanitized = sanitizeIdentifier(stringValue);
5170
+ const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
5171
+ let result;
5172
+ if (words.length === 0) {
5173
+ result = "Value";
5174
+ } else {
5175
+ result = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
5176
+ }
5035
5177
  if (/^\d/.test(result)) {
5036
5178
  result = `N${result}`;
5037
5179
  }
5038
5180
  if (!result || /^_+$/.test(result)) {
5039
- result = "Value";
5181
+ return "Value";
5040
5182
  }
5041
5183
  return result;
5042
5184
  }
@@ -5054,9 +5196,10 @@ var init_name_utils = __esm({
5054
5196
  // src/generators/enum-generator.ts
5055
5197
  function generateEnum(name, values, options) {
5056
5198
  const schemaName = `${toCamelCase(name, options)}Schema`;
5199
+ const typeName = toPascalCase(name);
5057
5200
  const enumValues = values.map((v) => `"${v}"`).join(", ");
5058
5201
  const schemaCode = `export const ${schemaName} = z.enum([${enumValues}]);`;
5059
- const typeCode = `export type ${name} = z.infer<typeof ${schemaName}>;`;
5202
+ const typeCode = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
5060
5203
  return { schemaCode, typeCode };
5061
5204
  }
5062
5205
  var init_enum_generator = __esm({
@@ -5181,6 +5324,9 @@ var init_lru_cache = __esm({
5181
5324
  this.cache = /* @__PURE__ */ new Map();
5182
5325
  this.maxSize = maxSize;
5183
5326
  }
5327
+ get capacity() {
5328
+ return this.maxSize;
5329
+ }
5184
5330
  get(key) {
5185
5331
  if (!this.cache.has(key)) return void 0;
5186
5332
  const value = this.cache.get(key);
@@ -5213,6 +5359,82 @@ var init_lru_cache = __esm({
5213
5359
  }
5214
5360
  });
5215
5361
 
5362
+ // 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)) {
5377
+ return true;
5378
+ }
5379
+ return false;
5380
+ }
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;
5394
+ }
5395
+ function stripPrefix(input, pattern, ensureLeadingChar) {
5396
+ if (!pattern) {
5397
+ return input;
5398
+ }
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
+ }
5411
+ }
5412
+ return stripped;
5413
+ }
5414
+ } else {
5415
+ const stringPattern = pattern;
5416
+ if (input.startsWith(stringPattern)) {
5417
+ const stripped = input.substring(stringPattern.length);
5418
+ if (ensureLeadingChar) {
5419
+ if (stripped === "") {
5420
+ return ensureLeadingChar;
5421
+ }
5422
+ if (!stripped.startsWith(ensureLeadingChar)) {
5423
+ return `${ensureLeadingChar}${stripped}`;
5424
+ }
5425
+ }
5426
+ return stripped;
5427
+ }
5428
+ }
5429
+ return input;
5430
+ }
5431
+ var init_pattern_utils = __esm({
5432
+ "src/utils/pattern-utils.ts"() {
5433
+ "use strict";
5434
+ init_esm_shims();
5435
+ }
5436
+ });
5437
+
5216
5438
  // src/validators/array-validator.ts
5217
5439
  function generateArrayValidation(schema, context) {
5218
5440
  var _a;
@@ -5766,6 +5988,11 @@ var init_object_validator = __esm({
5766
5988
  });
5767
5989
 
5768
5990
  // src/validators/string-validator.ts
5991
+ function configurePatternCache(size) {
5992
+ if (size > 0 && size !== PATTERN_CACHE.capacity) {
5993
+ PATTERN_CACHE = new LRUCache(size);
5994
+ }
5995
+ }
5769
5996
  function generateStringValidation(schema, useDescribe) {
5770
5997
  let validation = FORMAT_MAP[schema.format || ""] || "z.string()";
5771
5998
  if (schema.minLength !== void 0) {
@@ -5879,6 +6106,7 @@ var init_property_generator = __esm({
5879
6106
  init_esm_shims();
5880
6107
  init_lru_cache();
5881
6108
  init_name_utils();
6109
+ init_pattern_utils();
5882
6110
  init_string_utils();
5883
6111
  init_array_validator();
5884
6112
  init_composition_validator();
@@ -6121,8 +6349,9 @@ var init_property_generator = __esm({
6121
6349
  }
6122
6350
  (_a = this.context.schemaDependencies.get(currentSchema)) == null ? void 0 : _a.add(refName);
6123
6351
  }
6124
- const schemaName = `${toCamelCase(resolvedRefName, this.context.namingOptions)}Schema`;
6125
- if (currentSchema && this.isCircularThroughAlias(currentSchema, refName)) {
6352
+ const strippedRefName = stripPrefix(resolvedRefName, this.context.stripSchemaPrefix);
6353
+ const schemaName = `${toCamelCase(strippedRefName, this.context.namingOptions)}Schema`;
6354
+ if (currentSchema && (refName === currentSchema || this.isCircularThroughAlias(currentSchema, refName))) {
6126
6355
  const lazySchema = `z.lazy((): z.ZodTypeAny => ${schemaName})`;
6127
6356
  return wrapNullable(lazySchema, nullable);
6128
6357
  }
@@ -6411,6 +6640,7 @@ var init_operation_filters = __esm({
6411
6640
  // src/openapi-generator.ts
6412
6641
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
6413
6642
  import { dirname, normalize } from "path";
6643
+ import { minimatch as minimatch2 } from "minimatch";
6414
6644
  import { parse } from "yaml";
6415
6645
  var OpenApiGenerator;
6416
6646
  var init_openapi_generator = __esm({
@@ -6423,6 +6653,8 @@ var init_openapi_generator = __esm({
6423
6653
  init_property_generator();
6424
6654
  init_name_utils();
6425
6655
  init_operation_filters();
6656
+ init_pattern_utils();
6657
+ init_string_validator();
6426
6658
  OpenApiGenerator = class {
6427
6659
  constructor(options) {
6428
6660
  this.schemas = /* @__PURE__ */ new Map();
@@ -6431,7 +6663,7 @@ var init_openapi_generator = __esm({
6431
6663
  this.schemaUsageMap = /* @__PURE__ */ new Map();
6432
6664
  this.needsZodImport = true;
6433
6665
  this.filterStats = createFilterStatistics();
6434
- var _a, _b, _c;
6666
+ var _a, _b, _c, _d, _e;
6435
6667
  if (!options.input) {
6436
6668
  throw new ConfigurationError("Input path is required", { providedOptions: options });
6437
6669
  }
@@ -6444,11 +6676,18 @@ var init_openapi_generator = __esm({
6444
6676
  schemaType: options.schemaType || "all",
6445
6677
  prefix: options.prefix,
6446
6678
  suffix: options.suffix,
6679
+ stripSchemaPrefix: options.stripSchemaPrefix,
6447
6680
  showStats: (_c = options.showStats) != null ? _c : true,
6448
6681
  request: options.request,
6449
6682
  response: options.response,
6450
- operationFilters: options.operationFilters
6683
+ operationFilters: options.operationFilters,
6684
+ ignoreHeaders: options.ignoreHeaders,
6685
+ cacheSize: (_d = options.cacheSize) != null ? _d : 1e3,
6686
+ batchSize: (_e = options.batchSize) != null ? _e : 10
6451
6687
  };
6688
+ if (this.options.cacheSize) {
6689
+ configurePatternCache(this.options.cacheSize);
6690
+ }
6452
6691
  try {
6453
6692
  const fs = __require("fs");
6454
6693
  if (!fs.existsSync(this.options.input)) {
@@ -6514,7 +6753,8 @@ var init_openapi_generator = __esm({
6514
6753
  namingOptions: {
6515
6754
  prefix: this.options.prefix,
6516
6755
  suffix: this.options.suffix
6517
- }
6756
+ },
6757
+ stripSchemaPrefix: this.options.stripSchemaPrefix
6518
6758
  });
6519
6759
  }
6520
6760
  /**
@@ -6548,9 +6788,11 @@ var init_openapi_generator = __esm({
6548
6788
  const typeCode = this.types.get(name);
6549
6789
  if (schemaCode) {
6550
6790
  output.push(schemaCode);
6551
- if (!schemaCode.includes(`export type ${name}`)) {
6552
- const schemaName = `${toCamelCase(name, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
6553
- output.push(`export type ${name} = z.infer<typeof ${schemaName}>;`);
6791
+ const strippedName = stripPrefix(name, this.options.stripSchemaPrefix);
6792
+ const typeName = toPascalCase(strippedName);
6793
+ if (!schemaCode.includes(`export type ${typeName}`)) {
6794
+ const schemaName = `${toCamelCase(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
6795
+ output.push(`export type ${typeName} = z.infer<typeof ${schemaName}>;`);
6554
6796
  }
6555
6797
  output.push("");
6556
6798
  } else if (typeCode) {
@@ -6584,6 +6826,7 @@ var init_openapi_generator = __esm({
6584
6826
  const normalizedOutput = normalize(this.options.output);
6585
6827
  this.ensureDirectoryExists(normalizedOutput);
6586
6828
  writeFileSync(normalizedOutput, output);
6829
+ console.log(` \u2713 Generated ${normalizedOutput}`);
6587
6830
  }
6588
6831
  /**
6589
6832
  * Resolve options for a specific context (request or response)
@@ -6865,7 +7108,8 @@ var init_openapi_generator = __esm({
6865
7108
  const resolvedOptions = context === "response" ? this.responseOptions : this.requestOptions;
6866
7109
  if (schema.enum) {
6867
7110
  const jsdoc2 = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
6868
- const { schemaCode, typeCode } = generateEnum(name, schema.enum, {
7111
+ const strippedName2 = stripPrefix(name, this.options.stripSchemaPrefix);
7112
+ const { schemaCode, typeCode } = generateEnum(strippedName2, schema.enum, {
6869
7113
  prefix: this.options.prefix,
6870
7114
  suffix: this.options.suffix
6871
7115
  });
@@ -6874,7 +7118,8 @@ ${typeCode}`;
6874
7118
  this.schemas.set(name, enumSchemaCode);
6875
7119
  return;
6876
7120
  }
6877
- const schemaName = `${toCamelCase(name, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
7121
+ const strippedName = stripPrefix(name, this.options.stripSchemaPrefix);
7122
+ const schemaName = `${toCamelCase(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
6878
7123
  const jsdoc = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
6879
7124
  if (schema.allOf && schema.allOf.length === 1 && schema.allOf[0].$ref) {
6880
7125
  const refName = resolveRef(schema.allOf[0].$ref);
@@ -6890,7 +7135,8 @@ ${typeCode}`;
6890
7135
  namingOptions: {
6891
7136
  prefix: this.options.prefix,
6892
7137
  suffix: this.options.suffix
6893
- }
7138
+ },
7139
+ stripSchemaPrefix: this.options.stripSchemaPrefix
6894
7140
  });
6895
7141
  const isAlias = !!(schema.$ref && !schema.properties && !schema.allOf && !schema.oneOf && !schema.anyOf);
6896
7142
  const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, isAlias);
@@ -6994,6 +7240,24 @@ ${propsCode}
6994
7240
  }
6995
7241
  }
6996
7242
  }
7243
+ /**
7244
+ * Check if a header should be ignored based on filter patterns
7245
+ * @internal
7246
+ */
7247
+ shouldIgnoreHeader(headerName) {
7248
+ const ignorePatterns = this.options.ignoreHeaders;
7249
+ if (!ignorePatterns || ignorePatterns.length === 0) {
7250
+ return false;
7251
+ }
7252
+ if (ignorePatterns.includes("*")) {
7253
+ return true;
7254
+ }
7255
+ const headerLower = headerName.toLowerCase();
7256
+ return ignorePatterns.some((pattern) => {
7257
+ const patternLower = pattern.toLowerCase();
7258
+ return minimatch2(headerLower, patternLower);
7259
+ });
7260
+ }
6997
7261
  /**
6998
7262
  * Generate header parameter schemas for each operation
6999
7263
  * Header parameters are always string type (HTTP header semantics)
@@ -7016,7 +7280,7 @@ ${propsCode}
7016
7280
  continue;
7017
7281
  }
7018
7282
  const headerParams = operation.parameters.filter(
7019
- (param) => param && typeof param === "object" && param.in === "header"
7283
+ (param) => param && typeof param === "object" && param.in === "header" && !this.shouldIgnoreHeader(param.name)
7020
7284
  );
7021
7285
  if (headerParams.length === 0) {
7022
7286
  continue;
@@ -7074,7 +7338,8 @@ ${propsCode}
7074
7338
  generateQueryParamType(schema, param) {
7075
7339
  if (schema.$ref) {
7076
7340
  const refName = resolveRef(schema.$ref);
7077
- const schemaName = toCamelCase(refName, { prefix: this.options.prefix, suffix: this.options.suffix });
7341
+ const strippedRefName = stripPrefix(refName, this.options.stripSchemaPrefix);
7342
+ const schemaName = toCamelCase(strippedRefName, { prefix: this.options.prefix, suffix: this.options.suffix });
7078
7343
  return `${schemaName}Schema`;
7079
7344
  }
7080
7345
  if (schema.enum) {
@@ -7215,120 +7480,108 @@ ${propsCode}
7215
7480
  }
7216
7481
  });
7217
7482
 
7218
- // src/batch-executor.ts
7219
- async function processSpec(spec, index, total) {
7220
- console.log(`Processing [${index + 1}/${total}] ${spec.input}...`);
7221
- try {
7222
- const generator = new OpenApiGenerator(spec);
7223
- generator.generate();
7224
- console.log(`\u2713 Successfully generated ${spec.output}`);
7225
- return {
7226
- spec,
7227
- success: true
7228
- };
7229
- } catch (error) {
7230
- const errorMessage = error instanceof Error ? error.message : String(error);
7231
- console.error(`\u2717 Failed to generate ${spec.output}: ${errorMessage}`);
7232
- return {
7233
- spec,
7234
- success: false,
7235
- error: errorMessage
7236
- };
7237
- }
7238
- }
7239
- async function executeParallel(specs) {
7240
- console.log(`
7241
- Executing ${specs.length} spec(s) in parallel...
7242
- `);
7243
- const promises = specs.map((spec, index) => processSpec(spec, index, specs.length));
7244
- const results = await Promise.allSettled(promises);
7245
- return results.map((result, index) => {
7246
- if (result.status === "fulfilled") {
7247
- return result.value;
7248
- }
7249
- return {
7250
- spec: specs[index],
7251
- success: false,
7252
- error: result.reason instanceof Error ? result.reason.message : String(result.reason)
7253
- };
7254
- });
7255
- }
7256
- async function executeSequential(specs) {
7257
- console.log(`
7258
- Executing ${specs.length} spec(s) sequentially...
7259
- `);
7260
- const results = [];
7261
- for (let i = 0; i < specs.length; i++) {
7262
- const result = await processSpec(specs[i], i, specs.length);
7263
- results.push(result);
7483
+ // src/utils/config-schemas.ts
7484
+ import { z } from "zod";
7485
+ var RequestResponseOptionsSchema, OperationFiltersSchema;
7486
+ var init_config_schemas = __esm({
7487
+ "src/utils/config-schemas.ts"() {
7488
+ "use strict";
7489
+ init_esm_shims();
7490
+ RequestResponseOptionsSchema = z.strictObject({
7491
+ mode: z.enum(["strict", "normal", "loose"]).optional(),
7492
+ useDescribe: z.boolean().optional(),
7493
+ includeDescriptions: z.boolean().optional()
7494
+ });
7495
+ OperationFiltersSchema = z.strictObject({
7496
+ includeTags: z.array(z.string()).optional(),
7497
+ excludeTags: z.array(z.string()).optional(),
7498
+ includePaths: z.array(z.string()).optional(),
7499
+ excludePaths: z.array(z.string()).optional(),
7500
+ includeMethods: z.array(z.string()).optional(),
7501
+ excludeMethods: z.array(z.string()).optional(),
7502
+ includeOperationIds: z.array(z.string()).optional(),
7503
+ excludeOperationIds: z.array(z.string()).optional(),
7504
+ excludeDeprecated: z.boolean().optional()
7505
+ });
7264
7506
  }
7265
- return results;
7266
- }
7267
- function printSummary(summary) {
7268
- console.log(`
7269
- ${"=".repeat(50)}`);
7270
- console.log("Batch Execution Summary");
7271
- console.log("=".repeat(50));
7272
- console.log(`Total specs: ${summary.total}`);
7273
- console.log(`Successful: ${summary.successful}`);
7274
- console.log(`Failed: ${summary.failed}`);
7275
- if (summary.failed > 0) {
7276
- console.log("\nFailed specs:");
7277
- for (const result of summary.results) {
7278
- if (!result.success) {
7279
- console.error(` \u2717 ${result.spec.input}`);
7280
- console.error(` Error: ${result.error}`);
7281
- }
7282
- }
7507
+ });
7508
+
7509
+ // src/utils/config-validation.ts
7510
+ function formatConfigValidationError(error, filepath, configPath, additionalNotes) {
7511
+ var _a;
7512
+ const formattedErrors = ((_a = error.issues) == null ? void 0 : _a.map((err) => {
7513
+ const path2 = err.path.length > 0 ? err.path.join(".") : "root";
7514
+ return ` - ${path2}: ${err.message}`;
7515
+ }).join("\n")) || "Unknown validation error";
7516
+ const configSource = filepath || configPath || "config file";
7517
+ const lines = [
7518
+ `Invalid configuration file at: ${configSource}`,
7519
+ "",
7520
+ "Validation errors:",
7521
+ formattedErrors,
7522
+ "",
7523
+ "Please check your configuration file and ensure:",
7524
+ " - All required fields are present (specs array with input/output)",
7525
+ " - Field names are spelled correctly (no typos)",
7526
+ " - Values match the expected types (e.g., mode: 'strict' | 'normal' | 'loose')",
7527
+ " - No unknown/extra properties are included"
7528
+ ];
7529
+ if (additionalNotes && additionalNotes.length > 0) {
7530
+ lines.push(...additionalNotes.map((note) => ` - ${note}`));
7283
7531
  }
7284
- console.log(`${"=".repeat(50)}
7285
- `);
7532
+ return lines.join("\n");
7286
7533
  }
7287
- async function executeBatch(specs, executionMode = "parallel") {
7288
- if (specs.length === 0) {
7289
- throw new ConfigurationError("No specs provided for batch execution", { specsCount: 0, executionMode });
7534
+ var init_config_validation = __esm({
7535
+ "src/utils/config-validation.ts"() {
7536
+ "use strict";
7537
+ init_esm_shims();
7290
7538
  }
7291
- let results = [];
7292
- try {
7293
- results = executionMode === "parallel" ? await executeParallel(specs) : await executeSequential(specs);
7294
- const summary = {
7295
- total: results.length,
7296
- successful: results.filter((r) => r.success).length,
7297
- failed: results.filter((r) => !r.success).length,
7298
- results
7299
- };
7300
- printSummary(summary);
7301
- return summary;
7302
- } finally {
7303
- if (results.length > 10) {
7304
- for (const result of results) {
7305
- if (result.spec) {
7306
- result.spec = null;
7307
- }
7308
- }
7309
- if (global.gc) {
7310
- global.gc();
7311
- }
7539
+ });
7540
+
7541
+ // src/utils/typescript-loader.ts
7542
+ function createTypeScriptLoader() {
7543
+ return async (filepath) => {
7544
+ try {
7545
+ const esbuild = await import("esbuild");
7546
+ const fs = await import("fs");
7547
+ const path2 = await import("path");
7548
+ const tsCode = fs.readFileSync(filepath, "utf-8");
7549
+ const result = await esbuild.build({
7550
+ stdin: {
7551
+ contents: tsCode,
7552
+ loader: "ts",
7553
+ resolveDir: path2.dirname(filepath),
7554
+ sourcefile: filepath
7555
+ },
7556
+ format: "cjs",
7557
+ platform: "node",
7558
+ target: "node18",
7559
+ bundle: false,
7560
+ write: false
7561
+ });
7562
+ const jsCode = result.outputFiles[0].text;
7563
+ const module = { exports: {} };
7564
+ const func = new Function("exports", "module", "require", "__filename", "__dirname", jsCode);
7565
+ func(module.exports, module, __require, filepath, path2.dirname(filepath));
7566
+ return module.exports.default || module.exports;
7567
+ } catch (error) {
7568
+ throw new Error(
7569
+ `Failed to load TypeScript config from ${filepath}: ${error instanceof Error ? error.message : String(error)}`
7570
+ );
7312
7571
  }
7313
- }
7314
- }
7315
- function getBatchExitCode(summary) {
7316
- return summary.failed > 0 ? 1 : 0;
7572
+ };
7317
7573
  }
7318
- var init_batch_executor = __esm({
7319
- "src/batch-executor.ts"() {
7574
+ var init_typescript_loader = __esm({
7575
+ "src/utils/typescript-loader.ts"() {
7320
7576
  "use strict";
7321
7577
  init_esm_shims();
7322
- init_errors();
7323
- init_openapi_generator();
7324
7578
  }
7325
7579
  });
7326
7580
 
7327
7581
  // src/utils/config-loader.ts
7328
7582
  import { cosmiconfig } from "cosmiconfig";
7329
- import { z } from "zod";
7583
+ import { z as z2 } from "zod";
7330
7584
  async function loadConfig(configPath) {
7331
- var _a;
7332
7585
  const explorer = cosmiconfig("openapi-to-zod", {
7333
7586
  searchPlaces: ["openapi-to-zod.config.ts", "openapi-to-zod.config.json", "package.json"],
7334
7587
  loaders: {
@@ -7343,31 +7596,15 @@ async function loadConfig(configPath) {
7343
7596
  }
7344
7597
  if (!result || !result.config) {
7345
7598
  throw new Error(
7346
- configPath ? `Config file not found at: ${configPath}` : "No config file found. Searched for: openapi-to-zod.config.ts, openapi-to-zod.config.json, package.json (openapi-to-zod key)"
7599
+ configPath ? `Config file not found at: ${configPath}` : "No config file found. Searched for: openapi-to-zod.config.ts, openapi-to-zod.config.json, package.json (openapi-to-zod key)\nRun 'openapi-to-zod init' to create a new config file."
7347
7600
  );
7348
7601
  }
7349
7602
  try {
7350
7603
  const validatedConfig = ConfigFileSchema.parse(result.config);
7351
7604
  return validatedConfig;
7352
7605
  } catch (error) {
7353
- if (error instanceof z.ZodError) {
7354
- const formattedErrors = ((_a = error.issues) == null ? void 0 : _a.map((err) => {
7355
- const path2 = err.path.length > 0 ? err.path.join(".") : "root";
7356
- return ` - ${path2}: ${err.message}`;
7357
- }).join("\n")) || "Unknown validation error";
7358
- const configSource = result.filepath || configPath || "config file";
7359
- const errorMessage = [
7360
- `Invalid configuration file at: ${configSource}`,
7361
- "",
7362
- "Validation errors:",
7363
- formattedErrors,
7364
- "",
7365
- "Please check your configuration file and ensure:",
7366
- " - All required fields are present (specs array with input/output)",
7367
- " - Field names are spelled correctly (no typos)",
7368
- " - Values match the expected types (e.g., mode: 'strict' | 'normal' | 'loose')",
7369
- " - No unknown/extra properties are included"
7370
- ].join("\n");
7606
+ if (error instanceof z2.ZodError) {
7607
+ const errorMessage = formatConfigValidationError(error, result.filepath, configPath);
7371
7608
  throw new Error(errorMessage);
7372
7609
  }
7373
7610
  throw error;
@@ -7394,90 +7631,55 @@ function mergeConfigWithDefaults(config) {
7394
7631
  return merged;
7395
7632
  });
7396
7633
  }
7397
- var RequestResponseOptionsSchema, OperationFiltersSchema, OpenApiGeneratorOptionsSchema, ConfigFileSchema, createTypeScriptLoader;
7634
+ var OpenApiGeneratorOptionsSchema, ConfigFileSchema;
7398
7635
  var init_config_loader = __esm({
7399
7636
  "src/utils/config-loader.ts"() {
7400
7637
  "use strict";
7401
7638
  init_esm_shims();
7402
- RequestResponseOptionsSchema = z.strictObject({
7403
- mode: z.enum(["strict", "normal", "loose"]).optional(),
7404
- useDescribe: z.boolean().optional(),
7405
- includeDescriptions: z.boolean().optional()
7406
- });
7407
- OperationFiltersSchema = z.strictObject({
7408
- includeTags: z.array(z.string()).optional(),
7409
- excludeTags: z.array(z.string()).optional(),
7410
- includePaths: z.array(z.string()).optional(),
7411
- excludePaths: z.array(z.string()).optional(),
7412
- includeMethods: z.array(z.string()).optional(),
7413
- excludeMethods: z.array(z.string()).optional(),
7414
- includeOperationIds: z.array(z.string()).optional(),
7415
- excludeOperationIds: z.array(z.string()).optional(),
7416
- excludeDeprecated: z.boolean().optional()
7417
- });
7418
- OpenApiGeneratorOptionsSchema = z.strictObject({
7419
- mode: z.enum(["strict", "normal", "loose"]).optional(),
7420
- input: z.string(),
7421
- output: z.string(),
7422
- includeDescriptions: z.boolean().optional(),
7423
- useDescribe: z.boolean().optional(),
7424
- schemaType: z.enum(["all", "request", "response"]).optional(),
7425
- prefix: z.string().optional(),
7426
- suffix: z.string().optional(),
7427
- showStats: z.boolean().optional(),
7639
+ init_config_schemas();
7640
+ init_config_validation();
7641
+ init_typescript_loader();
7642
+ OpenApiGeneratorOptionsSchema = z2.strictObject({
7643
+ mode: z2.enum(["strict", "normal", "loose"]).optional(),
7644
+ input: z2.string(),
7645
+ output: z2.string(),
7646
+ includeDescriptions: z2.boolean().optional(),
7647
+ useDescribe: z2.boolean().optional(),
7648
+ schemaType: z2.enum(["all", "request", "response"]).optional(),
7649
+ prefix: z2.string().optional(),
7650
+ suffix: z2.string().optional(),
7651
+ stripSchemaPrefix: z2.string().optional(),
7652
+ showStats: z2.boolean().optional(),
7428
7653
  request: RequestResponseOptionsSchema.optional(),
7429
7654
  response: RequestResponseOptionsSchema.optional(),
7430
- name: z.string().optional(),
7431
- operationFilters: OperationFiltersSchema.optional()
7655
+ name: z2.string().optional(),
7656
+ operationFilters: OperationFiltersSchema.optional(),
7657
+ cacheSize: z2.number().positive().optional(),
7658
+ batchSize: z2.number().positive().optional()
7432
7659
  });
7433
- ConfigFileSchema = z.strictObject({
7434
- defaults: z.strictObject({
7435
- mode: z.enum(["strict", "normal", "loose"]).optional(),
7436
- includeDescriptions: z.boolean().optional(),
7437
- useDescribe: z.boolean().optional(),
7438
- schemaType: z.enum(["all", "request", "response"]).optional(),
7439
- prefix: z.string().optional(),
7440
- suffix: z.string().optional(),
7441
- showStats: z.boolean().optional(),
7660
+ ConfigFileSchema = z2.strictObject({
7661
+ defaults: z2.strictObject({
7662
+ mode: z2.enum(["strict", "normal", "loose"]).optional(),
7663
+ includeDescriptions: z2.boolean().optional(),
7664
+ useDescribe: z2.boolean().optional(),
7665
+ schemaType: z2.enum(["all", "request", "response"]).optional(),
7666
+ prefix: z2.string().optional(),
7667
+ suffix: z2.string().optional(),
7668
+ stripSchemaPrefix: z2.string().optional(),
7669
+ showStats: z2.boolean().optional(),
7442
7670
  request: RequestResponseOptionsSchema.optional(),
7443
7671
  response: RequestResponseOptionsSchema.optional(),
7444
- operationFilters: OperationFiltersSchema.optional()
7672
+ operationFilters: OperationFiltersSchema.optional(),
7673
+ cacheSize: z2.number().positive().optional(),
7674
+ batchSize: z2.number().positive().optional()
7445
7675
  }).optional(),
7446
- specs: z.array(OpenApiGeneratorOptionsSchema).min(1, "At least one spec is required"),
7447
- executionMode: z.enum(["parallel", "sequential"]).optional()
7676
+ specs: z2.array(OpenApiGeneratorOptionsSchema).min(1, {
7677
+ message: "Configuration must include at least one specification. Each specification should have 'input' and 'output' paths."
7678
+ }).refine((specs) => specs.every((spec) => spec.input && spec.output), {
7679
+ message: "Each specification must have both 'input' and 'output' paths defined"
7680
+ }),
7681
+ executionMode: z2.enum(["parallel", "sequential"]).optional()
7448
7682
  });
7449
- createTypeScriptLoader = () => {
7450
- return async (filepath) => {
7451
- try {
7452
- const esbuild = await import("esbuild");
7453
- const fs = await import("fs");
7454
- const path2 = await import("path");
7455
- const tsCode = fs.readFileSync(filepath, "utf-8");
7456
- const result = await esbuild.build({
7457
- stdin: {
7458
- contents: tsCode,
7459
- loader: "ts",
7460
- resolveDir: path2.dirname(filepath),
7461
- sourcefile: filepath
7462
- },
7463
- format: "cjs",
7464
- platform: "node",
7465
- target: "node18",
7466
- bundle: false,
7467
- write: false
7468
- });
7469
- const jsCode = result.outputFiles[0].text;
7470
- const module = { exports: {} };
7471
- const func = new Function("exports", "module", "require", "__filename", "__dirname", jsCode);
7472
- func(module.exports, module, __require, filepath, path2.dirname(filepath));
7473
- return module.exports.default || module.exports;
7474
- } catch (error) {
7475
- throw new Error(
7476
- `Failed to load TypeScript config from ${filepath}: ${error instanceof Error ? error.message : String(error)}`
7477
- );
7478
- }
7479
- };
7480
- };
7481
7683
  }
7482
7684
  });
7483
7685
 
@@ -7491,6 +7693,7 @@ var require_cli = __commonJS({
7491
7693
  var import_prompts = __toESM(require_prompts3());
7492
7694
  init_batch_executor();
7493
7695
  init_errors();
7696
+ init_openapi_generator();
7494
7697
  init_config_loader();
7495
7698
  var program = new Command();
7496
7699
  program.name("openapi-to-zod").description("Generate Zod v4 schemas from OpenAPI specifications").version("1.0.0").option("-c, --config <path>", "Path to config file (openapi-to-zod.config.{ts,json})").addHelpText(
@@ -7498,17 +7701,13 @@ var require_cli = __commonJS({
7498
7701
  `
7499
7702
  Examples:
7500
7703
  # Create a new config file
7501
- $ openapi-to-zod --init
7704
+ $ openapi-to-zod init
7502
7705
 
7503
7706
  # Generate with auto-discovered config
7504
7707
  $ openapi-to-zod
7505
7708
 
7506
7709
  # Generate with custom config path
7507
7710
  $ openapi-to-zod --config custom.config.ts
7508
-
7509
- Breaking Changes (v2.0):
7510
- CLI options removed. Use configuration file instead.
7511
- Run 'openapi-to-zod --init' to create a config file.
7512
7711
  `
7513
7712
  ).action(async (options) => {
7514
7713
  try {
@@ -7565,17 +7764,12 @@ Breaking Changes (v2.0):
7565
7764
  return { files, totalCount };
7566
7765
  }
7567
7766
  async function executeConfigMode(options) {
7568
- let config;
7569
- try {
7570
- config = await loadConfig(options.config);
7571
- } catch {
7572
- throw new CliOptionsError("No config file found. Run 'openapi-to-zod --init' to create one.", {
7573
- configPath: options.config
7574
- });
7575
- }
7767
+ var _a, _b;
7768
+ const config = await loadConfig(options.config);
7576
7769
  const specs = mergeConfigWithDefaults(config);
7577
7770
  const executionMode = config.executionMode || "parallel";
7578
- const summary = await executeBatch(specs, executionMode);
7771
+ const batchSize = (_b = (_a = specs[0]) == null ? void 0 : _a.batchSize) != null ? _b : 10;
7772
+ const summary = await executeBatch(specs, executionMode, (spec) => new OpenApiGenerator(spec), batchSize);
7579
7773
  const exitCode = getBatchExitCode(summary);
7580
7774
  if (exitCode !== 0) {
7581
7775
  process.exit(exitCode);
@@ -7699,7 +7893,7 @@ export default defineConfig({
7699
7893
  mode: 'strict',
7700
7894
  includeDescriptions: true,
7701
7895
  useDescribe: false,
7702
- showStats: false,
7896
+ showStats: true,
7703
7897
  schemaType: 'all',
7704
7898
  },
7705
7899
  specs: [