@cerios/openapi-to-zod 0.6.0 → 1.0.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.js CHANGED
@@ -5013,10 +5013,123 @@ var ConfigurationError = class extends GeneratorError {
5013
5013
  }
5014
5014
  };
5015
5015
 
5016
+ // src/batch-executor.ts
5017
+ async function processSpec(spec, index, total, createGenerator) {
5018
+ const specInput = spec.input || "spec";
5019
+ const specOutput = spec.output || "output";
5020
+ console.log(`Processing [${index + 1}/${total}] ${specInput}...`);
5021
+ try {
5022
+ const generator = createGenerator(spec);
5023
+ generator.generate();
5024
+ return {
5025
+ spec,
5026
+ success: true
5027
+ };
5028
+ } catch (error) {
5029
+ const errorMessage = error instanceof Error ? error.message : String(error);
5030
+ console.error(`\u2717 Failed to generate ${specOutput}: ${errorMessage}`);
5031
+ return {
5032
+ spec,
5033
+ success: false,
5034
+ error: errorMessage
5035
+ };
5036
+ }
5037
+ }
5038
+ async function executeParallel(specs, createGenerator, batchSize) {
5039
+ console.log(`
5040
+ Executing ${specs.length} specification(s) in parallel (batch size: ${batchSize})...
5041
+ `);
5042
+ const results = [];
5043
+ for (let i = 0; i < specs.length; i += batchSize) {
5044
+ const batch = specs.slice(i, Math.min(i + batchSize, specs.length));
5045
+ const batchPromises = batch.map(
5046
+ (spec, batchIndex) => processSpec(spec, i + batchIndex, specs.length, createGenerator)
5047
+ );
5048
+ const batchResults = await Promise.allSettled(batchPromises);
5049
+ for (let j = 0; j < batchResults.length; j++) {
5050
+ const result = batchResults[j];
5051
+ if (result.status === "fulfilled") {
5052
+ results.push(result.value);
5053
+ } else {
5054
+ results.push({
5055
+ spec: batch[j],
5056
+ success: false,
5057
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
5058
+ });
5059
+ }
5060
+ }
5061
+ }
5062
+ return results;
5063
+ }
5064
+ async function executeSequential(specs, createGenerator) {
5065
+ console.log(`
5066
+ Executing ${specs.length} spec(s) sequentially...
5067
+ `);
5068
+ const results = [];
5069
+ for (let i = 0; i < specs.length; i++) {
5070
+ const result = await processSpec(specs[i], i, specs.length, createGenerator);
5071
+ results.push(result);
5072
+ }
5073
+ return results;
5074
+ }
5075
+ function printSummary(summary) {
5076
+ console.log(`
5077
+ ${"=".repeat(50)}`);
5078
+ console.log("Batch Execution Summary");
5079
+ console.log("=".repeat(50));
5080
+ console.log(`Total specs: ${summary.total}`);
5081
+ console.log(`Successful: ${summary.successful}`);
5082
+ console.log(`Failed: ${summary.failed}`);
5083
+ if (summary.failed > 0) {
5084
+ console.log("\nFailed specs:");
5085
+ for (const result of summary.results) {
5086
+ if (!result.success) {
5087
+ const specInput = result.spec.input || "spec";
5088
+ console.error(` \u2717 ${specInput}`);
5089
+ console.error(` Error: ${result.error}`);
5090
+ }
5091
+ }
5092
+ }
5093
+ console.log(`${"=".repeat(50)}
5094
+ `);
5095
+ }
5096
+ async function executeBatch(specs, executionMode = "parallel", createGenerator, batchSize) {
5097
+ if (specs.length === 0) {
5098
+ throw new ConfigurationError("No specs provided for batch execution", { specsCount: 0, executionMode });
5099
+ }
5100
+ let results = [];
5101
+ try {
5102
+ results = executionMode === "parallel" ? await executeParallel(specs, createGenerator, batchSize) : await executeSequential(specs, createGenerator);
5103
+ const summary = {
5104
+ total: results.length,
5105
+ successful: results.filter((r) => r.success).length,
5106
+ failed: results.filter((r) => !r.success).length,
5107
+ results
5108
+ };
5109
+ printSummary(summary);
5110
+ return summary;
5111
+ } finally {
5112
+ if (results.length > batchSize) {
5113
+ for (const result of results) {
5114
+ if (result.spec) {
5115
+ result.spec = null;
5116
+ }
5117
+ }
5118
+ if (global.gc) {
5119
+ global.gc();
5120
+ }
5121
+ }
5122
+ }
5123
+ }
5124
+ function getBatchExitCode(summary) {
5125
+ return summary.failed > 0 ? 1 : 0;
5126
+ }
5127
+
5016
5128
  // src/openapi-generator.ts
5017
5129
  init_cjs_shims();
5018
5130
  var import_node_fs = require("fs");
5019
5131
  var import_node_path = require("path");
5132
+ var import_minimatch2 = require("minimatch");
5020
5133
  var import_yaml = require("yaml");
5021
5134
 
5022
5135
  // src/generators/enum-generator.ts
@@ -5165,6 +5278,9 @@ var LRUCache = class {
5165
5278
  this.cache = /* @__PURE__ */ new Map();
5166
5279
  this.maxSize = maxSize;
5167
5280
  }
5281
+ get capacity() {
5282
+ return this.maxSize;
5283
+ }
5168
5284
  get(key) {
5169
5285
  if (!this.cache.has(key)) return void 0;
5170
5286
  const value = this.cache.get(key);
@@ -5722,6 +5838,11 @@ ${properties.join(",\n")}
5722
5838
  // src/validators/string-validator.ts
5723
5839
  init_cjs_shims();
5724
5840
  var PATTERN_CACHE = new LRUCache(1e3);
5841
+ function configurePatternCache(size) {
5842
+ if (size > 0 && size !== PATTERN_CACHE.capacity) {
5843
+ PATTERN_CACHE = new LRUCache(size);
5844
+ }
5845
+ }
5725
5846
  var FORMAT_MAP = {
5726
5847
  uuid: "z.uuid()",
5727
5848
  email: "z.email()",
@@ -6343,7 +6464,7 @@ var OpenApiGenerator = class {
6343
6464
  this.schemaUsageMap = /* @__PURE__ */ new Map();
6344
6465
  this.needsZodImport = true;
6345
6466
  this.filterStats = createFilterStatistics();
6346
- var _a, _b, _c;
6467
+ var _a, _b, _c, _d, _e;
6347
6468
  if (!options.input) {
6348
6469
  throw new ConfigurationError("Input path is required", { providedOptions: options });
6349
6470
  }
@@ -6359,8 +6480,14 @@ var OpenApiGenerator = class {
6359
6480
  showStats: (_c = options.showStats) != null ? _c : true,
6360
6481
  request: options.request,
6361
6482
  response: options.response,
6362
- operationFilters: options.operationFilters
6483
+ operationFilters: options.operationFilters,
6484
+ ignoreHeaders: options.ignoreHeaders,
6485
+ cacheSize: (_d = options.cacheSize) != null ? _d : 1e3,
6486
+ batchSize: (_e = options.batchSize) != null ? _e : 10
6363
6487
  };
6488
+ if (this.options.cacheSize) {
6489
+ configurePatternCache(this.options.cacheSize);
6490
+ }
6364
6491
  try {
6365
6492
  const fs = require("fs");
6366
6493
  if (!fs.existsSync(this.options.input)) {
@@ -6496,6 +6623,7 @@ var OpenApiGenerator = class {
6496
6623
  const normalizedOutput = (0, import_node_path.normalize)(this.options.output);
6497
6624
  this.ensureDirectoryExists(normalizedOutput);
6498
6625
  (0, import_node_fs.writeFileSync)(normalizedOutput, output);
6626
+ console.log(` \u2713 Generated ${normalizedOutput}`);
6499
6627
  }
6500
6628
  /**
6501
6629
  * Resolve options for a specific context (request or response)
@@ -6906,6 +7034,24 @@ ${propsCode}
6906
7034
  }
6907
7035
  }
6908
7036
  }
7037
+ /**
7038
+ * Check if a header should be ignored based on filter patterns
7039
+ * @internal
7040
+ */
7041
+ shouldIgnoreHeader(headerName) {
7042
+ const ignorePatterns = this.options.ignoreHeaders;
7043
+ if (!ignorePatterns || ignorePatterns.length === 0) {
7044
+ return false;
7045
+ }
7046
+ if (ignorePatterns.includes("*")) {
7047
+ return true;
7048
+ }
7049
+ const headerLower = headerName.toLowerCase();
7050
+ return ignorePatterns.some((pattern) => {
7051
+ const patternLower = pattern.toLowerCase();
7052
+ return (0, import_minimatch2.minimatch)(headerLower, patternLower);
7053
+ });
7054
+ }
6909
7055
  /**
6910
7056
  * Generate header parameter schemas for each operation
6911
7057
  * Header parameters are always string type (HTTP header semantics)
@@ -6928,7 +7074,7 @@ ${propsCode}
6928
7074
  continue;
6929
7075
  }
6930
7076
  const headerParams = operation.parameters.filter(
6931
- (param) => param && typeof param === "object" && param.in === "header"
7077
+ (param) => param && typeof param === "object" && param.in === "header" && !this.shouldIgnoreHeader(param.name)
6932
7078
  );
6933
7079
  if (headerParams.length === 0) {
6934
7080
  continue;
@@ -7125,110 +7271,13 @@ ${propsCode}
7125
7271
  }
7126
7272
  };
7127
7273
 
7128
- // src/batch-executor.ts
7129
- async function processSpec(spec, index, total) {
7130
- console.log(`Processing [${index + 1}/${total}] ${spec.input}...`);
7131
- try {
7132
- const generator = new OpenApiGenerator(spec);
7133
- generator.generate();
7134
- console.log(`\u2713 Successfully generated ${spec.output}`);
7135
- return {
7136
- spec,
7137
- success: true
7138
- };
7139
- } catch (error) {
7140
- const errorMessage = error instanceof Error ? error.message : String(error);
7141
- console.error(`\u2717 Failed to generate ${spec.output}: ${errorMessage}`);
7142
- return {
7143
- spec,
7144
- success: false,
7145
- error: errorMessage
7146
- };
7147
- }
7148
- }
7149
- async function executeParallel(specs) {
7150
- console.log(`
7151
- Executing ${specs.length} spec(s) in parallel...
7152
- `);
7153
- const promises = specs.map((spec, index) => processSpec(spec, index, specs.length));
7154
- const results = await Promise.allSettled(promises);
7155
- return results.map((result, index) => {
7156
- if (result.status === "fulfilled") {
7157
- return result.value;
7158
- }
7159
- return {
7160
- spec: specs[index],
7161
- success: false,
7162
- error: result.reason instanceof Error ? result.reason.message : String(result.reason)
7163
- };
7164
- });
7165
- }
7166
- async function executeSequential(specs) {
7167
- console.log(`
7168
- Executing ${specs.length} spec(s) sequentially...
7169
- `);
7170
- const results = [];
7171
- for (let i = 0; i < specs.length; i++) {
7172
- const result = await processSpec(specs[i], i, specs.length);
7173
- results.push(result);
7174
- }
7175
- return results;
7176
- }
7177
- function printSummary(summary) {
7178
- console.log(`
7179
- ${"=".repeat(50)}`);
7180
- console.log("Batch Execution Summary");
7181
- console.log("=".repeat(50));
7182
- console.log(`Total specs: ${summary.total}`);
7183
- console.log(`Successful: ${summary.successful}`);
7184
- console.log(`Failed: ${summary.failed}`);
7185
- if (summary.failed > 0) {
7186
- console.log("\nFailed specs:");
7187
- for (const result of summary.results) {
7188
- if (!result.success) {
7189
- console.error(` \u2717 ${result.spec.input}`);
7190
- console.error(` Error: ${result.error}`);
7191
- }
7192
- }
7193
- }
7194
- console.log(`${"=".repeat(50)}
7195
- `);
7196
- }
7197
- async function executeBatch(specs, executionMode = "parallel") {
7198
- if (specs.length === 0) {
7199
- throw new ConfigurationError("No specs provided for batch execution", { specsCount: 0, executionMode });
7200
- }
7201
- let results = [];
7202
- try {
7203
- results = executionMode === "parallel" ? await executeParallel(specs) : await executeSequential(specs);
7204
- const summary = {
7205
- total: results.length,
7206
- successful: results.filter((r) => r.success).length,
7207
- failed: results.filter((r) => !r.success).length,
7208
- results
7209
- };
7210
- printSummary(summary);
7211
- return summary;
7212
- } finally {
7213
- if (results.length > 10) {
7214
- for (const result of results) {
7215
- if (result.spec) {
7216
- result.spec = null;
7217
- }
7218
- }
7219
- if (global.gc) {
7220
- global.gc();
7221
- }
7222
- }
7223
- }
7224
- }
7225
- function getBatchExitCode(summary) {
7226
- return summary.failed > 0 ? 1 : 0;
7227
- }
7228
-
7229
7274
  // src/utils/config-loader.ts
7230
7275
  init_cjs_shims();
7231
7276
  var import_cosmiconfig = require("cosmiconfig");
7277
+ var import_zod2 = require("zod");
7278
+
7279
+ // src/utils/config-schemas.ts
7280
+ init_cjs_shims();
7232
7281
  var import_zod = require("zod");
7233
7282
  var RequestResponseOptionsSchema = import_zod.z.strictObject({
7234
7283
  mode: import_zod.z.enum(["strict", "normal", "loose"]).optional(),
@@ -7246,38 +7295,37 @@ var OperationFiltersSchema = import_zod.z.strictObject({
7246
7295
  excludeOperationIds: import_zod.z.array(import_zod.z.string()).optional(),
7247
7296
  excludeDeprecated: import_zod.z.boolean().optional()
7248
7297
  });
7249
- var OpenApiGeneratorOptionsSchema = import_zod.z.strictObject({
7250
- mode: import_zod.z.enum(["strict", "normal", "loose"]).optional(),
7251
- input: import_zod.z.string(),
7252
- output: import_zod.z.string(),
7253
- includeDescriptions: import_zod.z.boolean().optional(),
7254
- useDescribe: import_zod.z.boolean().optional(),
7255
- schemaType: import_zod.z.enum(["all", "request", "response"]).optional(),
7256
- prefix: import_zod.z.string().optional(),
7257
- suffix: import_zod.z.string().optional(),
7258
- showStats: import_zod.z.boolean().optional(),
7259
- request: RequestResponseOptionsSchema.optional(),
7260
- response: RequestResponseOptionsSchema.optional(),
7261
- name: import_zod.z.string().optional(),
7262
- operationFilters: OperationFiltersSchema.optional()
7263
- });
7264
- var ConfigFileSchema = import_zod.z.strictObject({
7265
- defaults: import_zod.z.strictObject({
7266
- mode: import_zod.z.enum(["strict", "normal", "loose"]).optional(),
7267
- includeDescriptions: import_zod.z.boolean().optional(),
7268
- useDescribe: import_zod.z.boolean().optional(),
7269
- schemaType: import_zod.z.enum(["all", "request", "response"]).optional(),
7270
- prefix: import_zod.z.string().optional(),
7271
- suffix: import_zod.z.string().optional(),
7272
- showStats: import_zod.z.boolean().optional(),
7273
- request: RequestResponseOptionsSchema.optional(),
7274
- response: RequestResponseOptionsSchema.optional(),
7275
- operationFilters: OperationFiltersSchema.optional()
7276
- }).optional(),
7277
- specs: import_zod.z.array(OpenApiGeneratorOptionsSchema).min(1, "At least one spec is required"),
7278
- executionMode: import_zod.z.enum(["parallel", "sequential"]).optional()
7279
- });
7280
- var createTypeScriptLoader = () => {
7298
+
7299
+ // src/utils/config-validation.ts
7300
+ init_cjs_shims();
7301
+ function formatConfigValidationError(error, filepath, configPath, additionalNotes) {
7302
+ var _a;
7303
+ const formattedErrors = ((_a = error.issues) == null ? void 0 : _a.map((err) => {
7304
+ const path = err.path.length > 0 ? err.path.join(".") : "root";
7305
+ return ` - ${path}: ${err.message}`;
7306
+ }).join("\n")) || "Unknown validation error";
7307
+ const configSource = filepath || configPath || "config file";
7308
+ const lines = [
7309
+ `Invalid configuration file at: ${configSource}`,
7310
+ "",
7311
+ "Validation errors:",
7312
+ formattedErrors,
7313
+ "",
7314
+ "Please check your configuration file and ensure:",
7315
+ " - All required fields are present (specs array with input/output)",
7316
+ " - Field names are spelled correctly (no typos)",
7317
+ " - Values match the expected types (e.g., mode: 'strict' | 'normal' | 'loose')",
7318
+ " - No unknown/extra properties are included"
7319
+ ];
7320
+ if (additionalNotes && additionalNotes.length > 0) {
7321
+ lines.push(...additionalNotes.map((note) => ` - ${note}`));
7322
+ }
7323
+ return lines.join("\n");
7324
+ }
7325
+
7326
+ // src/utils/typescript-loader.ts
7327
+ init_cjs_shims();
7328
+ function createTypeScriptLoader() {
7281
7329
  return async (filepath) => {
7282
7330
  try {
7283
7331
  const esbuild = await import("esbuild");
@@ -7308,9 +7356,49 @@ var createTypeScriptLoader = () => {
7308
7356
  );
7309
7357
  }
7310
7358
  };
7311
- };
7359
+ }
7360
+
7361
+ // src/utils/config-loader.ts
7362
+ var OpenApiGeneratorOptionsSchema = import_zod2.z.strictObject({
7363
+ mode: import_zod2.z.enum(["strict", "normal", "loose"]).optional(),
7364
+ input: import_zod2.z.string(),
7365
+ output: import_zod2.z.string(),
7366
+ includeDescriptions: import_zod2.z.boolean().optional(),
7367
+ useDescribe: import_zod2.z.boolean().optional(),
7368
+ schemaType: import_zod2.z.enum(["all", "request", "response"]).optional(),
7369
+ prefix: import_zod2.z.string().optional(),
7370
+ suffix: import_zod2.z.string().optional(),
7371
+ showStats: import_zod2.z.boolean().optional(),
7372
+ request: RequestResponseOptionsSchema.optional(),
7373
+ response: RequestResponseOptionsSchema.optional(),
7374
+ name: import_zod2.z.string().optional(),
7375
+ operationFilters: OperationFiltersSchema.optional(),
7376
+ cacheSize: import_zod2.z.number().positive().optional(),
7377
+ batchSize: import_zod2.z.number().positive().optional()
7378
+ });
7379
+ var ConfigFileSchema = import_zod2.z.strictObject({
7380
+ defaults: import_zod2.z.strictObject({
7381
+ mode: import_zod2.z.enum(["strict", "normal", "loose"]).optional(),
7382
+ includeDescriptions: import_zod2.z.boolean().optional(),
7383
+ useDescribe: import_zod2.z.boolean().optional(),
7384
+ schemaType: import_zod2.z.enum(["all", "request", "response"]).optional(),
7385
+ prefix: import_zod2.z.string().optional(),
7386
+ suffix: import_zod2.z.string().optional(),
7387
+ showStats: import_zod2.z.boolean().optional(),
7388
+ request: RequestResponseOptionsSchema.optional(),
7389
+ response: RequestResponseOptionsSchema.optional(),
7390
+ operationFilters: OperationFiltersSchema.optional(),
7391
+ cacheSize: import_zod2.z.number().positive().optional(),
7392
+ batchSize: import_zod2.z.number().positive().optional()
7393
+ }).optional(),
7394
+ specs: import_zod2.z.array(OpenApiGeneratorOptionsSchema).min(1, {
7395
+ message: "Configuration must include at least one specification. Each specification should have 'input' and 'output' paths."
7396
+ }).refine((specs) => specs.every((spec) => spec.input && spec.output), {
7397
+ message: "Each specification must have both 'input' and 'output' paths defined"
7398
+ }),
7399
+ executionMode: import_zod2.z.enum(["parallel", "sequential"]).optional()
7400
+ });
7312
7401
  async function loadConfig(configPath) {
7313
- var _a;
7314
7402
  const explorer = (0, import_cosmiconfig.cosmiconfig)("openapi-to-zod", {
7315
7403
  searchPlaces: ["openapi-to-zod.config.ts", "openapi-to-zod.config.json", "package.json"],
7316
7404
  loaders: {
@@ -7332,24 +7420,8 @@ async function loadConfig(configPath) {
7332
7420
  const validatedConfig = ConfigFileSchema.parse(result.config);
7333
7421
  return validatedConfig;
7334
7422
  } catch (error) {
7335
- if (error instanceof import_zod.z.ZodError) {
7336
- const formattedErrors = ((_a = error.issues) == null ? void 0 : _a.map((err) => {
7337
- const path = err.path.length > 0 ? err.path.join(".") : "root";
7338
- return ` - ${path}: ${err.message}`;
7339
- }).join("\n")) || "Unknown validation error";
7340
- const configSource = result.filepath || configPath || "config file";
7341
- const errorMessage = [
7342
- `Invalid configuration file at: ${configSource}`,
7343
- "",
7344
- "Validation errors:",
7345
- formattedErrors,
7346
- "",
7347
- "Please check your configuration file and ensure:",
7348
- " - All required fields are present (specs array with input/output)",
7349
- " - Field names are spelled correctly (no typos)",
7350
- " - Values match the expected types (e.g., mode: 'strict' | 'normal' | 'loose')",
7351
- " - No unknown/extra properties are included"
7352
- ].join("\n");
7423
+ if (error instanceof import_zod2.z.ZodError) {
7424
+ const errorMessage = formatConfigValidationError(error, result.filepath, configPath);
7353
7425
  throw new Error(errorMessage);
7354
7426
  }
7355
7427
  throw error;
@@ -7384,17 +7456,13 @@ program.name("openapi-to-zod").description("Generate Zod v4 schemas from OpenAPI
7384
7456
  `
7385
7457
  Examples:
7386
7458
  # Create a new config file
7387
- $ openapi-to-zod --init
7459
+ $ openapi-to-zod init
7388
7460
 
7389
7461
  # Generate with auto-discovered config
7390
7462
  $ openapi-to-zod
7391
7463
 
7392
7464
  # Generate with custom config path
7393
7465
  $ openapi-to-zod --config custom.config.ts
7394
-
7395
- Breaking Changes (v2.0):
7396
- CLI options removed. Use configuration file instead.
7397
- Run 'openapi-to-zod --init' to create a config file.
7398
7466
  `
7399
7467
  ).action(async (options) => {
7400
7468
  try {
@@ -7451,17 +7519,19 @@ function findSpecFiles() {
7451
7519
  return { files, totalCount };
7452
7520
  }
7453
7521
  async function executeConfigMode(options) {
7522
+ var _a, _b;
7454
7523
  let config;
7455
7524
  try {
7456
7525
  config = await loadConfig(options.config);
7457
7526
  } catch {
7458
- throw new CliOptionsError("No config file found. Run 'openapi-to-zod --init' to create one.", {
7527
+ throw new CliOptionsError("No config file found. Run 'openapi-to-zod init' to create one.", {
7459
7528
  configPath: options.config
7460
7529
  });
7461
7530
  }
7462
7531
  const specs = mergeConfigWithDefaults(config);
7463
7532
  const executionMode = config.executionMode || "parallel";
7464
- const summary = await executeBatch(specs, executionMode);
7533
+ const batchSize = (_b = (_a = specs[0]) == null ? void 0 : _a.batchSize) != null ? _b : 10;
7534
+ const summary = await executeBatch(specs, executionMode, (spec) => new OpenApiGenerator(spec), batchSize);
7465
7535
  const exitCode = getBatchExitCode(summary);
7466
7536
  if (exitCode !== 0) {
7467
7537
  process.exit(exitCode);
@@ -7585,7 +7655,7 @@ export default defineConfig({
7585
7655
  mode: 'strict',
7586
7656
  includeDescriptions: true,
7587
7657
  useDescribe: false,
7588
- showStats: false,
7658
+ showStats: true,
7589
7659
  schemaType: 'all',
7590
7660
  },
7591
7661
  specs: [