@cerios/openapi-to-zod 0.3.0 → 0.4.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/README.md CHANGED
@@ -834,14 +834,14 @@ All errors include:
834
834
 
835
835
  ## API Reference
836
836
 
837
- ### `generateZodSchemas(options: GeneratorOptions): void`
837
+ ### `generateZodSchemas(options: OpenApiGeneratorOptions): void`
838
838
 
839
839
  Main function to generate schemas.
840
840
 
841
841
  #### Options
842
842
 
843
843
  ```typescript
844
- interface GeneratorOptions {
844
+ interface OpenApiGeneratorOptions {
845
845
  /**
846
846
  * Object validation mode
847
847
  * - 'strict': Uses z.strictObject() - no additional properties allowed
package/dist/cli.js CHANGED
@@ -4961,6 +4961,7 @@ var require_prompts3 = __commonJS({
4961
4961
  // src/cli.ts
4962
4962
  init_cjs_shims();
4963
4963
  var import_node_fs2 = require("fs");
4964
+ var import_node_path2 = require("path");
4964
4965
  var import_commander = require("commander");
4965
4966
  var import_prompts = __toESM(require_prompts3());
4966
4967
 
@@ -5012,7 +5013,7 @@ var ConfigurationError = class extends GeneratorError {
5012
5013
  }
5013
5014
  };
5014
5015
 
5015
- // src/generator.ts
5016
+ // src/openapi-generator.ts
5016
5017
  init_cjs_shims();
5017
5018
  var import_node_fs = require("fs");
5018
5019
  var import_node_path = require("path");
@@ -6221,8 +6222,8 @@ _PropertyGenerator.INCLUSION_RULES = {
6221
6222
  };
6222
6223
  var PropertyGenerator = _PropertyGenerator;
6223
6224
 
6224
- // src/generator.ts
6225
- var ZodSchemaGenerator = class {
6225
+ // src/openapi-generator.ts
6226
+ var OpenApiGenerator = class {
6226
6227
  constructor(options) {
6227
6228
  this.schemas = /* @__PURE__ */ new Map();
6228
6229
  this.types = /* @__PURE__ */ new Map();
@@ -6262,19 +6263,41 @@ var ZodSchemaGenerator = class {
6262
6263
  }
6263
6264
  }
6264
6265
  try {
6265
- const yamlContent = (0, import_node_fs.readFileSync)(this.options.input, "utf-8");
6266
- this.spec = (0, import_yaml.parse)(yamlContent);
6266
+ const content = (0, import_node_fs.readFileSync)(this.options.input, "utf-8");
6267
+ try {
6268
+ this.spec = (0, import_yaml.parse)(content);
6269
+ } catch (yamlError) {
6270
+ try {
6271
+ this.spec = JSON.parse(content);
6272
+ } catch {
6273
+ if (yamlError instanceof Error) {
6274
+ const errorMessage = [
6275
+ `Failed to parse OpenAPI specification from: ${this.options.input}`,
6276
+ "",
6277
+ `Error: ${yamlError.message}`,
6278
+ "",
6279
+ "Please ensure:",
6280
+ " - The file exists and is readable",
6281
+ " - The file contains valid YAML or JSON syntax",
6282
+ " - The file is a valid OpenAPI 3.x specification"
6283
+ ].join("\n");
6284
+ throw new SpecValidationError(errorMessage, {
6285
+ filePath: this.options.input,
6286
+ originalError: yamlError.message
6287
+ });
6288
+ }
6289
+ throw yamlError;
6290
+ }
6291
+ }
6267
6292
  } catch (error) {
6293
+ if (error instanceof SpecValidationError) {
6294
+ throw error;
6295
+ }
6268
6296
  if (error instanceof Error) {
6269
6297
  const errorMessage = [
6270
- `Failed to parse OpenAPI specification from: ${this.options.input}`,
6271
- "",
6272
- `Error: ${error.message}`,
6298
+ `Failed to read OpenAPI specification from: ${this.options.input}`,
6273
6299
  "",
6274
- "Please ensure:",
6275
- " - The file exists and is readable",
6276
- " - The file contains valid YAML syntax",
6277
- " - The file is a valid OpenAPI 3.x specification"
6300
+ `Error: ${error.message}`
6278
6301
  ].join("\n");
6279
6302
  throw new SpecValidationError(errorMessage, { filePath: this.options.input, originalError: error.message });
6280
6303
  }
@@ -7136,7 +7159,7 @@ ${props.join("\n")}
7136
7159
  async function processSpec(spec, index, total) {
7137
7160
  console.log(`Processing [${index + 1}/${total}] ${spec.input}...`);
7138
7161
  try {
7139
- const generator = new ZodSchemaGenerator(spec);
7162
+ const generator = new OpenApiGenerator(spec);
7140
7163
  generator.generate();
7141
7164
  console.log(`\u2713 Successfully generated ${spec.output}`);
7142
7165
  return {
@@ -7247,7 +7270,7 @@ var RequestResponseOptionsSchema = import_zod.z.strictObject({
7247
7270
  typeMode: TypeModeSchema.optional(),
7248
7271
  nativeEnumType: NativeEnumTypeSchema.optional()
7249
7272
  });
7250
- var GeneratorOptionsSchema = import_zod.z.strictObject({
7273
+ var OpenApiGeneratorOptionsSchema = import_zod.z.strictObject({
7251
7274
  mode: import_zod.z.enum(["strict", "normal", "loose"]).optional(),
7252
7275
  input: import_zod.z.string(),
7253
7276
  output: import_zod.z.string(),
@@ -7277,7 +7300,7 @@ var ConfigFileSchema = import_zod.z.strictObject({
7277
7300
  request: RequestResponseOptionsSchema.optional(),
7278
7301
  response: RequestResponseOptionsSchema.optional()
7279
7302
  }).optional(),
7280
- specs: import_zod.z.array(GeneratorOptionsSchema).min(1, "At least one spec is required"),
7303
+ specs: import_zod.z.array(OpenApiGeneratorOptionsSchema).min(1, "At least one spec is required"),
7281
7304
  executionMode: import_zod.z.enum(["parallel", "sequential"]).optional()
7282
7305
  });
7283
7306
  var createTypeScriptLoader = () => {
@@ -7424,6 +7447,36 @@ program.command("init").description("Initialize a new openapi-to-zod configurati
7424
7447
  }
7425
7448
  });
7426
7449
  program.parse();
7450
+ function findSpecFiles() {
7451
+ const specFolders = ["spec", "specs"];
7452
+ const validExtensions = [".yaml", ".yml", ".json"];
7453
+ const excludePatterns = ["node_modules", ".git", "dist", "build", "coverage"];
7454
+ const allFiles = [];
7455
+ for (const folder of specFolders) {
7456
+ if (!(0, import_node_fs2.existsSync)(folder)) continue;
7457
+ try {
7458
+ const entries = (0, import_node_fs2.readdirSync)(folder, { recursive: true, encoding: "utf-8" });
7459
+ for (const entry of entries) {
7460
+ const fullPath = (0, import_node_path2.join)(folder, entry);
7461
+ if (excludePatterns.some((pattern) => fullPath.includes(pattern))) continue;
7462
+ try {
7463
+ const stats = (0, import_node_fs2.statSync)(fullPath);
7464
+ if (!stats.isFile()) continue;
7465
+ const hasValidExt = validExtensions.some((ext) => fullPath.endsWith(ext));
7466
+ if (!hasValidExt) continue;
7467
+ const sizeKB = (stats.size / 1024).toFixed(2);
7468
+ allFiles.push({ path: fullPath.replace(/\\/g, "/"), size: `${sizeKB} KB` });
7469
+ } catch {
7470
+ }
7471
+ }
7472
+ } catch {
7473
+ }
7474
+ }
7475
+ allFiles.sort((a, b) => a.path.localeCompare(b.path));
7476
+ const totalCount = allFiles.length;
7477
+ const files = allFiles.slice(0, 20);
7478
+ return { files, totalCount };
7479
+ }
7427
7480
  async function executeConfigMode(options) {
7428
7481
  let config;
7429
7482
  try {
@@ -7457,14 +7510,66 @@ async function initConfigFile() {
7457
7510
  return;
7458
7511
  }
7459
7512
  }
7460
- const response = await (0, import_prompts.default)([
7461
- {
7513
+ const { files, totalCount } = findSpecFiles();
7514
+ if (totalCount > 20) {
7515
+ console.log(`Showing first 20 of ${totalCount} files found. Use manual entry to specify others.
7516
+ `);
7517
+ }
7518
+ let inputPath;
7519
+ if (files.length > 0) {
7520
+ const choices = [
7521
+ ...files.map((f) => ({ title: `${f.path} (${f.size})`, value: f.path })),
7522
+ { title: "\u2192 Enter manually...", value: "__MANUAL__" }
7523
+ ];
7524
+ const inputResponse = await (0, import_prompts.default)({
7525
+ type: "select",
7526
+ name: "input",
7527
+ message: "Select OpenAPI spec file (YAML or JSON):",
7528
+ choices
7529
+ });
7530
+ if (!inputResponse.input) {
7531
+ console.log("\nInitialization cancelled.");
7532
+ return;
7533
+ }
7534
+ if (inputResponse.input === "__MANUAL__") {
7535
+ const manualResponse = await (0, import_prompts.default)({
7536
+ type: "text",
7537
+ name: "input",
7538
+ message: "Input OpenAPI file path (YAML or JSON):",
7539
+ initial: "openapi.{yaml,yml,json}",
7540
+ validate: (value) => {
7541
+ if (value.length === 0) return "Input path is required";
7542
+ if (!(0, import_node_fs2.existsSync)(value)) return "\u26A0\uFE0F File does not exist. Continue anyway?";
7543
+ return true;
7544
+ }
7545
+ });
7546
+ if (!manualResponse.input) {
7547
+ console.log("\nInitialization cancelled.");
7548
+ return;
7549
+ }
7550
+ inputPath = manualResponse.input;
7551
+ } else {
7552
+ inputPath = inputResponse.input;
7553
+ }
7554
+ } else {
7555
+ const manualResponse = await (0, import_prompts.default)({
7462
7556
  type: "text",
7463
7557
  name: "input",
7464
- message: "Input OpenAPI file path:",
7465
- initial: "openapi.yaml",
7466
- validate: (value) => value.length > 0 || "Input path is required"
7467
- },
7558
+ message: "Input OpenAPI file path (YAML or JSON):",
7559
+ initial: "openapi.{yaml,yml,json}",
7560
+ validate: (value) => {
7561
+ if (value.length === 0) return "Input path is required";
7562
+ if (!(0, import_node_fs2.existsSync)(value)) return "\u26A0\uFE0F File does not exist. Continue anyway?";
7563
+ return true;
7564
+ }
7565
+ });
7566
+ if (!manualResponse.input) {
7567
+ console.log("\nInitialization cancelled.");
7568
+ return;
7569
+ }
7570
+ inputPath = manualResponse.input;
7571
+ }
7572
+ const response = await (0, import_prompts.default)([
7468
7573
  {
7469
7574
  type: "text",
7470
7575
  name: "output",
@@ -7489,11 +7594,12 @@ async function initConfigFile() {
7489
7594
  initial: true
7490
7595
  }
7491
7596
  ]);
7492
- if (!response.input || !response.output || !response.format) {
7597
+ if (!response.output || !response.format) {
7493
7598
  console.log("\nInitialization cancelled.");
7494
7599
  return;
7495
7600
  }
7496
- const { input, output, format, includeDefaults } = response;
7601
+ const { output, format, includeDefaults } = response;
7602
+ const input = inputPath;
7497
7603
  let configContent;
7498
7604
  let configFilename;
7499
7605
  if (format === "ts") {