@cerios/openapi-to-zod 0.3.0 → 0.5.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
@@ -6291,13 +6291,153 @@ var init_property_generator = __esm({
6291
6291
  }
6292
6292
  });
6293
6293
 
6294
- // src/generator.ts
6294
+ // src/utils/operation-filters.ts
6295
+ import { minimatch } from "minimatch";
6296
+ function createFilterStatistics() {
6297
+ return {
6298
+ totalOperations: 0,
6299
+ includedOperations: 0,
6300
+ filteredByTags: 0,
6301
+ filteredByPaths: 0,
6302
+ filteredByMethods: 0,
6303
+ filteredByOperationIds: 0,
6304
+ filteredByDeprecated: 0
6305
+ };
6306
+ }
6307
+ function matchesAnyPattern(value, patterns) {
6308
+ if (!patterns || patterns.length === 0) {
6309
+ return false;
6310
+ }
6311
+ if (!value) {
6312
+ return false;
6313
+ }
6314
+ return patterns.some((pattern) => minimatch(value, pattern));
6315
+ }
6316
+ function containsAny(arr, values) {
6317
+ if (!values || values.length === 0) {
6318
+ return false;
6319
+ }
6320
+ if (!arr || arr.length === 0) {
6321
+ return false;
6322
+ }
6323
+ return values.some((value) => arr.includes(value));
6324
+ }
6325
+ function shouldIncludeOperation(operation, path2, method, filters, stats) {
6326
+ if (!filters) {
6327
+ return true;
6328
+ }
6329
+ const methodLower = method.toLowerCase();
6330
+ const operationId = operation == null ? void 0 : operation.operationId;
6331
+ const tags = (operation == null ? void 0 : operation.tags) || [];
6332
+ const deprecated = (operation == null ? void 0 : operation.deprecated) === true;
6333
+ if (filters.includeTags && filters.includeTags.length > 0) {
6334
+ if (!containsAny(tags, filters.includeTags)) {
6335
+ if (stats) stats.filteredByTags++;
6336
+ return false;
6337
+ }
6338
+ }
6339
+ if (filters.includePaths && filters.includePaths.length > 0) {
6340
+ if (!matchesAnyPattern(path2, filters.includePaths)) {
6341
+ if (stats) stats.filteredByPaths++;
6342
+ return false;
6343
+ }
6344
+ }
6345
+ if (filters.includeMethods && filters.includeMethods.length > 0) {
6346
+ const methodsLower = filters.includeMethods.map((m) => m.toLowerCase());
6347
+ if (!methodsLower.includes(methodLower)) {
6348
+ if (stats) stats.filteredByMethods++;
6349
+ return false;
6350
+ }
6351
+ }
6352
+ if (filters.includeOperationIds && filters.includeOperationIds.length > 0) {
6353
+ if (!matchesAnyPattern(operationId, filters.includeOperationIds)) {
6354
+ if (stats) stats.filteredByOperationIds++;
6355
+ return false;
6356
+ }
6357
+ }
6358
+ if (filters.excludeDeprecated === true && deprecated) {
6359
+ if (stats) stats.filteredByDeprecated++;
6360
+ return false;
6361
+ }
6362
+ if (filters.excludeTags && filters.excludeTags.length > 0) {
6363
+ if (containsAny(tags, filters.excludeTags)) {
6364
+ if (stats) stats.filteredByTags++;
6365
+ return false;
6366
+ }
6367
+ }
6368
+ if (filters.excludePaths && filters.excludePaths.length > 0) {
6369
+ if (matchesAnyPattern(path2, filters.excludePaths)) {
6370
+ if (stats) stats.filteredByPaths++;
6371
+ return false;
6372
+ }
6373
+ }
6374
+ if (filters.excludeMethods && filters.excludeMethods.length > 0) {
6375
+ const methodsLower = filters.excludeMethods.map((m) => m.toLowerCase());
6376
+ if (methodsLower.includes(methodLower)) {
6377
+ if (stats) stats.filteredByMethods++;
6378
+ return false;
6379
+ }
6380
+ }
6381
+ if (filters.excludeOperationIds && filters.excludeOperationIds.length > 0) {
6382
+ if (matchesAnyPattern(operationId, filters.excludeOperationIds)) {
6383
+ if (stats) stats.filteredByOperationIds++;
6384
+ return false;
6385
+ }
6386
+ }
6387
+ return true;
6388
+ }
6389
+ function validateFilters(stats, filters) {
6390
+ if (!filters || stats.totalOperations === 0) {
6391
+ return;
6392
+ }
6393
+ if (stats.includedOperations === 0) {
6394
+ console.warn(
6395
+ `\u26A0\uFE0F Warning: All ${stats.totalOperations} operations were filtered out. Check your operationFilters configuration.`
6396
+ );
6397
+ const filterBreakdown = [];
6398
+ if (stats.filteredByTags > 0) filterBreakdown.push(`${stats.filteredByTags} by tags`);
6399
+ if (stats.filteredByPaths > 0) filterBreakdown.push(`${stats.filteredByPaths} by paths`);
6400
+ if (stats.filteredByMethods > 0) filterBreakdown.push(`${stats.filteredByMethods} by methods`);
6401
+ if (stats.filteredByOperationIds > 0) filterBreakdown.push(`${stats.filteredByOperationIds} by operationIds`);
6402
+ if (stats.filteredByDeprecated > 0) filterBreakdown.push(`${stats.filteredByDeprecated} by deprecated flag`);
6403
+ if (filterBreakdown.length > 0) {
6404
+ console.warn(` Filtered: ${filterBreakdown.join(", ")}`);
6405
+ }
6406
+ }
6407
+ }
6408
+ function formatFilterStatistics(stats) {
6409
+ if (stats.totalOperations === 0) {
6410
+ return "";
6411
+ }
6412
+ const lines = [];
6413
+ lines.push("Operation Filtering:");
6414
+ lines.push(` Total operations: ${stats.totalOperations}`);
6415
+ lines.push(` Included operations: ${stats.includedOperations}`);
6416
+ const filteredCount = stats.filteredByTags + stats.filteredByPaths + stats.filteredByMethods + stats.filteredByOperationIds + stats.filteredByDeprecated;
6417
+ if (filteredCount > 0) {
6418
+ lines.push(` Filtered operations: ${filteredCount}`);
6419
+ if (stats.filteredByTags > 0) lines.push(` - By tags: ${stats.filteredByTags}`);
6420
+ if (stats.filteredByPaths > 0) lines.push(` - By paths: ${stats.filteredByPaths}`);
6421
+ if (stats.filteredByMethods > 0) lines.push(` - By methods: ${stats.filteredByMethods}`);
6422
+ if (stats.filteredByOperationIds > 0) lines.push(` - By operationIds: ${stats.filteredByOperationIds}`);
6423
+ if (stats.filteredByDeprecated > 0) lines.push(` - By deprecated: ${stats.filteredByDeprecated}`);
6424
+ }
6425
+ return lines.join("\n");
6426
+ }
6427
+ var init_operation_filters = __esm({
6428
+ "src/utils/operation-filters.ts"() {
6429
+ "use strict";
6430
+ init_esm_shims();
6431
+ }
6432
+ });
6433
+
6434
+ // src/openapi-generator.ts
6295
6435
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
6296
6436
  import { dirname, normalize } from "path";
6297
6437
  import { parse } from "yaml";
6298
- var ZodSchemaGenerator;
6299
- var init_generator = __esm({
6300
- "src/generator.ts"() {
6438
+ var OpenApiGenerator;
6439
+ var init_openapi_generator = __esm({
6440
+ "src/openapi-generator.ts"() {
6301
6441
  "use strict";
6302
6442
  init_esm_shims();
6303
6443
  init_errors();
@@ -6305,7 +6445,8 @@ var init_generator = __esm({
6305
6445
  init_jsdoc_generator();
6306
6446
  init_property_generator();
6307
6447
  init_name_utils();
6308
- ZodSchemaGenerator = class {
6448
+ init_operation_filters();
6449
+ OpenApiGenerator = class {
6309
6450
  constructor(options) {
6310
6451
  this.schemas = /* @__PURE__ */ new Map();
6311
6452
  this.types = /* @__PURE__ */ new Map();
@@ -6315,6 +6456,7 @@ var init_generator = __esm({
6315
6456
  this.schemaUsageMap = /* @__PURE__ */ new Map();
6316
6457
  this.schemaTypeModeMap = /* @__PURE__ */ new Map();
6317
6458
  this.needsZodImport = false;
6459
+ this.filterStats = createFilterStatistics();
6318
6460
  var _a, _b, _c;
6319
6461
  if (!options.input) {
6320
6462
  throw new ConfigurationError("Input path is required", { providedOptions: options });
@@ -6332,7 +6474,8 @@ var init_generator = __esm({
6332
6474
  showStats: (_c = options.showStats) != null ? _c : true,
6333
6475
  nativeEnumType: options.nativeEnumType || "union",
6334
6476
  request: options.request,
6335
- response: options.response
6477
+ response: options.response,
6478
+ operationFilters: options.operationFilters
6336
6479
  };
6337
6480
  try {
6338
6481
  const fs = __require("fs");
@@ -6345,19 +6488,41 @@ var init_generator = __esm({
6345
6488
  }
6346
6489
  }
6347
6490
  try {
6348
- const yamlContent = readFileSync(this.options.input, "utf-8");
6349
- this.spec = parse(yamlContent);
6491
+ const content = readFileSync(this.options.input, "utf-8");
6492
+ try {
6493
+ this.spec = parse(content);
6494
+ } catch (yamlError) {
6495
+ try {
6496
+ this.spec = JSON.parse(content);
6497
+ } catch {
6498
+ if (yamlError instanceof Error) {
6499
+ const errorMessage = [
6500
+ `Failed to parse OpenAPI specification from: ${this.options.input}`,
6501
+ "",
6502
+ `Error: ${yamlError.message}`,
6503
+ "",
6504
+ "Please ensure:",
6505
+ " - The file exists and is readable",
6506
+ " - The file contains valid YAML or JSON syntax",
6507
+ " - The file is a valid OpenAPI 3.x specification"
6508
+ ].join("\n");
6509
+ throw new SpecValidationError(errorMessage, {
6510
+ filePath: this.options.input,
6511
+ originalError: yamlError.message
6512
+ });
6513
+ }
6514
+ throw yamlError;
6515
+ }
6516
+ }
6350
6517
  } catch (error) {
6518
+ if (error instanceof SpecValidationError) {
6519
+ throw error;
6520
+ }
6351
6521
  if (error instanceof Error) {
6352
6522
  const errorMessage = [
6353
- `Failed to parse OpenAPI specification from: ${this.options.input}`,
6354
- "",
6355
- `Error: ${error.message}`,
6523
+ `Failed to read OpenAPI specification from: ${this.options.input}`,
6356
6524
  "",
6357
- "Please ensure:",
6358
- " - The file exists and is readable",
6359
- " - The file contains valid YAML syntax",
6360
- " - The file is a valid OpenAPI 3.x specification"
6525
+ `Error: ${error.message}`
6361
6526
  ].join("\n");
6362
6527
  throw new SpecValidationError(errorMessage, { filePath: this.options.input, originalError: error.message });
6363
6528
  }
@@ -6415,6 +6580,8 @@ var init_generator = __esm({
6415
6580
  this.generateComponentSchema(name, schema);
6416
6581
  }
6417
6582
  this.generateQueryParameterSchemas();
6583
+ this.generateHeaderParameterSchemas();
6584
+ validateFilters(this.filterStats, this.options.operationFilters);
6418
6585
  const orderedSchemaNames = this.topologicalSort();
6419
6586
  const output = ["// Auto-generated by @cerios/openapi-to-zod", "// Do not edit this file manually", ""];
6420
6587
  if (this.options.showStats === true) {
@@ -6508,9 +6675,16 @@ var init_generator = __esm({
6508
6675
  const requestSchemas = /* @__PURE__ */ new Set();
6509
6676
  const responseSchemas = /* @__PURE__ */ new Set();
6510
6677
  if (this.spec.paths) {
6511
- for (const [, pathItem] of Object.entries(this.spec.paths)) {
6512
- for (const [, operation] of Object.entries(pathItem)) {
6678
+ for (const [path2, pathItem] of Object.entries(this.spec.paths)) {
6679
+ const methods = ["get", "post", "put", "patch", "delete", "head", "options"];
6680
+ for (const method of methods) {
6681
+ const operation = pathItem[method];
6513
6682
  if (typeof operation !== "object" || !operation) continue;
6683
+ this.filterStats.totalOperations++;
6684
+ if (!shouldIncludeOperation(operation, path2, method, this.options.operationFilters, this.filterStats)) {
6685
+ continue;
6686
+ }
6687
+ this.filterStats.includedOperations++;
6514
6688
  if ("requestBody" in operation && operation.requestBody && typeof operation.requestBody === "object" && "content" in operation.requestBody && operation.requestBody.content) {
6515
6689
  for (const mediaType of Object.values(operation.requestBody.content)) {
6516
6690
  if (mediaType && typeof mediaType === "object" && "schema" in mediaType && mediaType.schema) {
@@ -6860,12 +7034,15 @@ ${typeCode}`;
6860
7034
  if (!this.spec.paths) {
6861
7035
  return;
6862
7036
  }
6863
- for (const [_path, pathItem] of Object.entries(this.spec.paths)) {
7037
+ for (const [path2, pathItem] of Object.entries(this.spec.paths)) {
6864
7038
  if (!pathItem || typeof pathItem !== "object") continue;
6865
7039
  const methods = ["get", "post", "put", "patch", "delete", "head", "options"];
6866
7040
  for (const method of methods) {
6867
7041
  const operation = pathItem[method];
6868
7042
  if (!operation) continue;
7043
+ if (!shouldIncludeOperation(operation, path2, method, this.options.operationFilters)) {
7044
+ continue;
7045
+ }
6869
7046
  if (!operation.operationId || !operation.parameters || !Array.isArray(operation.parameters)) {
6870
7047
  continue;
6871
7048
  }
@@ -6926,6 +7103,80 @@ ${propsCode}
6926
7103
  const jsdoc = `/**
6927
7104
  * Query parameters for ${operation.operationId}
6928
7105
  */
7106
+ `;
7107
+ const fullSchemaCode = `${jsdoc}export const ${camelCaseSchemaName} = ${schemaCode};`;
7108
+ this.schemas.set(schemaName, fullSchemaCode);
7109
+ this.needsZodImport = true;
7110
+ }
7111
+ }
7112
+ }
7113
+ /**
7114
+ * Generate header parameter schemas for each operation
7115
+ * Header parameters are always string type (HTTP header semantics)
7116
+ */
7117
+ generateHeaderParameterSchemas() {
7118
+ var _a;
7119
+ if (!this.spec.paths) {
7120
+ return;
7121
+ }
7122
+ for (const [path2, pathItem] of Object.entries(this.spec.paths)) {
7123
+ if (!pathItem || typeof pathItem !== "object") continue;
7124
+ const methods = ["get", "post", "put", "patch", "delete", "head", "options"];
7125
+ for (const method of methods) {
7126
+ const operation = pathItem[method];
7127
+ if (!operation) continue;
7128
+ if (!shouldIncludeOperation(operation, path2, method, this.options.operationFilters)) {
7129
+ continue;
7130
+ }
7131
+ if (!operation.operationId || !operation.parameters || !Array.isArray(operation.parameters)) {
7132
+ continue;
7133
+ }
7134
+ const headerParams = operation.parameters.filter(
7135
+ (param) => param && typeof param === "object" && param.in === "header"
7136
+ );
7137
+ if (headerParams.length === 0) {
7138
+ continue;
7139
+ }
7140
+ const pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
7141
+ const schemaName = `${pascalOperationId}HeaderParams`;
7142
+ if (!this.schemaDependencies.has(schemaName)) {
7143
+ this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
7144
+ }
7145
+ const properties = {};
7146
+ for (const param of headerParams) {
7147
+ const paramName = param.name;
7148
+ const paramSchema = param.schema;
7149
+ if (!paramSchema) continue;
7150
+ let zodType = "z.string()";
7151
+ if (param.description && this.requestOptions.includeDescriptions) {
7152
+ if (this.requestOptions.useDescribe) {
7153
+ zodType = `${zodType}.describe(${JSON.stringify(param.description)})`;
7154
+ }
7155
+ }
7156
+ zodType = `${zodType}.optional()`;
7157
+ properties[paramName] = zodType;
7158
+ if (paramSchema.$ref) {
7159
+ const refName = resolveRef(paramSchema.$ref);
7160
+ (_a = this.schemaDependencies.get(schemaName)) == null ? void 0 : _a.add(refName);
7161
+ }
7162
+ }
7163
+ const objectMode = this.requestOptions.mode;
7164
+ const zodMethod = objectMode === "strict" ? "strictObject" : objectMode === "loose" ? "looseObject" : "object";
7165
+ const propsCode = Object.entries(properties).map(([key, value]) => {
7166
+ const needsQuotes = !/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key);
7167
+ const quotedKey = needsQuotes ? `"${key}"` : key;
7168
+ return ` ${quotedKey}: ${value}`;
7169
+ }).join(",\n");
7170
+ const schemaCode = `z.${zodMethod}({
7171
+ ${propsCode}
7172
+ })`;
7173
+ const operationName = pascalOperationId;
7174
+ const prefixedName = this.options.prefix ? `${toPascalCase(this.options.prefix)}${operationName}` : operationName;
7175
+ const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
7176
+ const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}HeaderParamsSchema`;
7177
+ const jsdoc = `/**
7178
+ * Header parameters for ${operation.operationId}
7179
+ */
6929
7180
  `;
6930
7181
  const fullSchemaCode = `${jsdoc}export const ${camelCaseSchemaName} = ${schemaCode};`;
6931
7182
  this.schemas.set(schemaName, fullSchemaCode);
@@ -7203,15 +7454,23 @@ ${props.join("\n")}
7203
7454
  stats.withConstraints++;
7204
7455
  }
7205
7456
  }
7206
- return [
7457
+ const output = [
7207
7458
  "// Generation Statistics:",
7208
7459
  `// Total schemas: ${stats.totalSchemas}`,
7209
7460
  `// Enums: ${stats.enums}`,
7210
7461
  `// Circular references: ${stats.withCircularRefs}`,
7211
7462
  `// Discriminated unions: ${stats.withDiscriminators}`,
7212
- `// With constraints: ${stats.withConstraints}`,
7213
- `// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`
7463
+ `// With constraints: ${stats.withConstraints}`
7214
7464
  ];
7465
+ if (this.options.operationFilters && this.filterStats.totalOperations > 0) {
7466
+ output.push("//");
7467
+ const filterStatsStr = formatFilterStatistics(this.filterStats);
7468
+ for (const line of filterStatsStr.split("\n")) {
7469
+ output.push(`// ${line}`);
7470
+ }
7471
+ }
7472
+ output.push(`// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`);
7473
+ return output;
7215
7474
  }
7216
7475
  };
7217
7476
  }
@@ -7221,7 +7480,7 @@ ${props.join("\n")}
7221
7480
  async function processSpec(spec, index, total) {
7222
7481
  console.log(`Processing [${index + 1}/${total}] ${spec.input}...`);
7223
7482
  try {
7224
- const generator = new ZodSchemaGenerator(spec);
7483
+ const generator = new OpenApiGenerator(spec);
7225
7484
  generator.generate();
7226
7485
  console.log(`\u2713 Successfully generated ${spec.output}`);
7227
7486
  return {
@@ -7322,7 +7581,7 @@ var init_batch_executor = __esm({
7322
7581
  "use strict";
7323
7582
  init_esm_shims();
7324
7583
  init_errors();
7325
- init_generator();
7584
+ init_openapi_generator();
7326
7585
  }
7327
7586
  });
7328
7587
 
@@ -7397,7 +7656,7 @@ function mergeConfigWithDefaults(config) {
7397
7656
  return merged;
7398
7657
  });
7399
7658
  }
7400
- var TypeModeSchema, NativeEnumTypeSchema, RequestResponseOptionsSchema, GeneratorOptionsSchema, ConfigFileSchema, createTypeScriptLoader;
7659
+ var TypeModeSchema, NativeEnumTypeSchema, RequestResponseOptionsSchema, OperationFiltersSchema, OpenApiGeneratorOptionsSchema, ConfigFileSchema, createTypeScriptLoader;
7401
7660
  var init_config_loader = __esm({
7402
7661
  "src/utils/config-loader.ts"() {
7403
7662
  "use strict";
@@ -7412,7 +7671,18 @@ var init_config_loader = __esm({
7412
7671
  typeMode: TypeModeSchema.optional(),
7413
7672
  nativeEnumType: NativeEnumTypeSchema.optional()
7414
7673
  });
7415
- GeneratorOptionsSchema = z.strictObject({
7674
+ OperationFiltersSchema = z.strictObject({
7675
+ includeTags: z.array(z.string()).optional(),
7676
+ excludeTags: z.array(z.string()).optional(),
7677
+ includePaths: z.array(z.string()).optional(),
7678
+ excludePaths: z.array(z.string()).optional(),
7679
+ includeMethods: z.array(z.string()).optional(),
7680
+ excludeMethods: z.array(z.string()).optional(),
7681
+ includeOperationIds: z.array(z.string()).optional(),
7682
+ excludeOperationIds: z.array(z.string()).optional(),
7683
+ excludeDeprecated: z.boolean().optional()
7684
+ });
7685
+ OpenApiGeneratorOptionsSchema = z.strictObject({
7416
7686
  mode: z.enum(["strict", "normal", "loose"]).optional(),
7417
7687
  input: z.string(),
7418
7688
  output: z.string(),
@@ -7426,7 +7696,8 @@ var init_config_loader = __esm({
7426
7696
  nativeEnumType: NativeEnumTypeSchema.optional(),
7427
7697
  request: RequestResponseOptionsSchema.optional(),
7428
7698
  response: RequestResponseOptionsSchema.optional(),
7429
- name: z.string().optional()
7699
+ name: z.string().optional(),
7700
+ operationFilters: OperationFiltersSchema.optional()
7430
7701
  });
7431
7702
  ConfigFileSchema = z.strictObject({
7432
7703
  defaults: z.strictObject({
@@ -7440,9 +7711,10 @@ var init_config_loader = __esm({
7440
7711
  showStats: z.boolean().optional(),
7441
7712
  nativeEnumType: NativeEnumTypeSchema.optional(),
7442
7713
  request: RequestResponseOptionsSchema.optional(),
7443
- response: RequestResponseOptionsSchema.optional()
7714
+ response: RequestResponseOptionsSchema.optional(),
7715
+ operationFilters: OperationFiltersSchema.optional()
7444
7716
  }).optional(),
7445
- specs: z.array(GeneratorOptionsSchema).min(1, "At least one spec is required"),
7717
+ specs: z.array(OpenApiGeneratorOptionsSchema).min(1, "At least one spec is required"),
7446
7718
  executionMode: z.enum(["parallel", "sequential"]).optional()
7447
7719
  });
7448
7720
  createTypeScriptLoader = () => {
@@ -7481,7 +7753,8 @@ var init_config_loader = __esm({
7481
7753
  });
7482
7754
 
7483
7755
  // src/cli.ts
7484
- import { existsSync as existsSync2, writeFileSync as writeFileSync2 } from "fs";
7756
+ import { existsSync as existsSync2, readdirSync, statSync, writeFileSync as writeFileSync2 } from "fs";
7757
+ import { join } from "path";
7485
7758
  import { Command } from "commander";
7486
7759
  var require_cli = __commonJS({
7487
7760
  "src/cli.ts"() {
@@ -7532,6 +7805,36 @@ Breaking Changes (v2.0):
7532
7805
  }
7533
7806
  });
7534
7807
  program.parse();
7808
+ function findSpecFiles() {
7809
+ const specFolders = ["spec", "specs"];
7810
+ const validExtensions = [".yaml", ".yml", ".json"];
7811
+ const excludePatterns = ["node_modules", ".git", "dist", "build", "coverage"];
7812
+ const allFiles = [];
7813
+ for (const folder of specFolders) {
7814
+ if (!existsSync2(folder)) continue;
7815
+ try {
7816
+ const entries = readdirSync(folder, { recursive: true, encoding: "utf-8" });
7817
+ for (const entry of entries) {
7818
+ const fullPath = join(folder, entry);
7819
+ if (excludePatterns.some((pattern) => fullPath.includes(pattern))) continue;
7820
+ try {
7821
+ const stats = statSync(fullPath);
7822
+ if (!stats.isFile()) continue;
7823
+ const hasValidExt = validExtensions.some((ext) => fullPath.endsWith(ext));
7824
+ if (!hasValidExt) continue;
7825
+ const sizeKB = (stats.size / 1024).toFixed(2);
7826
+ allFiles.push({ path: fullPath.replace(/\\/g, "/"), size: `${sizeKB} KB` });
7827
+ } catch {
7828
+ }
7829
+ }
7830
+ } catch {
7831
+ }
7832
+ }
7833
+ allFiles.sort((a, b) => a.path.localeCompare(b.path));
7834
+ const totalCount = allFiles.length;
7835
+ const files = allFiles.slice(0, 20);
7836
+ return { files, totalCount };
7837
+ }
7535
7838
  async function executeConfigMode(options) {
7536
7839
  let config;
7537
7840
  try {
@@ -7565,14 +7868,66 @@ Breaking Changes (v2.0):
7565
7868
  return;
7566
7869
  }
7567
7870
  }
7568
- const response = await (0, import_prompts.default)([
7569
- {
7871
+ const { files, totalCount } = findSpecFiles();
7872
+ if (totalCount > 20) {
7873
+ console.log(`Showing first 20 of ${totalCount} files found. Use manual entry to specify others.
7874
+ `);
7875
+ }
7876
+ let inputPath;
7877
+ if (files.length > 0) {
7878
+ const choices = [
7879
+ ...files.map((f) => ({ title: `${f.path} (${f.size})`, value: f.path })),
7880
+ { title: "\u2192 Enter manually...", value: "__MANUAL__" }
7881
+ ];
7882
+ const inputResponse = await (0, import_prompts.default)({
7883
+ type: "select",
7884
+ name: "input",
7885
+ message: "Select OpenAPI spec file (YAML or JSON):",
7886
+ choices
7887
+ });
7888
+ if (!inputResponse.input) {
7889
+ console.log("\nInitialization cancelled.");
7890
+ return;
7891
+ }
7892
+ if (inputResponse.input === "__MANUAL__") {
7893
+ const manualResponse = await (0, import_prompts.default)({
7894
+ type: "text",
7895
+ name: "input",
7896
+ message: "Input OpenAPI file path (YAML or JSON):",
7897
+ initial: "openapi.{yaml,yml,json}",
7898
+ validate: (value) => {
7899
+ if (value.length === 0) return "Input path is required";
7900
+ if (!existsSync2(value)) return "\u26A0\uFE0F File does not exist. Continue anyway?";
7901
+ return true;
7902
+ }
7903
+ });
7904
+ if (!manualResponse.input) {
7905
+ console.log("\nInitialization cancelled.");
7906
+ return;
7907
+ }
7908
+ inputPath = manualResponse.input;
7909
+ } else {
7910
+ inputPath = inputResponse.input;
7911
+ }
7912
+ } else {
7913
+ const manualResponse = await (0, import_prompts.default)({
7570
7914
  type: "text",
7571
7915
  name: "input",
7572
- message: "Input OpenAPI file path:",
7573
- initial: "openapi.yaml",
7574
- validate: (value) => value.length > 0 || "Input path is required"
7575
- },
7916
+ message: "Input OpenAPI file path (YAML or JSON):",
7917
+ initial: "openapi.{yaml,yml,json}",
7918
+ validate: (value) => {
7919
+ if (value.length === 0) return "Input path is required";
7920
+ if (!existsSync2(value)) return "\u26A0\uFE0F File does not exist. Continue anyway?";
7921
+ return true;
7922
+ }
7923
+ });
7924
+ if (!manualResponse.input) {
7925
+ console.log("\nInitialization cancelled.");
7926
+ return;
7927
+ }
7928
+ inputPath = manualResponse.input;
7929
+ }
7930
+ const response = await (0, import_prompts.default)([
7576
7931
  {
7577
7932
  type: "text",
7578
7933
  name: "output",
@@ -7597,11 +7952,12 @@ Breaking Changes (v2.0):
7597
7952
  initial: true
7598
7953
  }
7599
7954
  ]);
7600
- if (!response.input || !response.output || !response.format) {
7955
+ if (!response.output || !response.format) {
7601
7956
  console.log("\nInitialization cancelled.");
7602
7957
  return;
7603
7958
  }
7604
- const { input, output, format, includeDefaults } = response;
7959
+ const { output, format, includeDefaults } = response;
7960
+ const input = inputPath;
7605
7961
  let configContent;
7606
7962
  let configFilename;
7607
7963
  if (format === "ts") {
@@ -7662,7 +8018,21 @@ export default defineConfig({
7662
8018
  console.log("\nNext steps:");
7663
8019
  console.log(" 1. Review and customize your config file if needed");
7664
8020
  console.log(" 2. Run 'openapi-to-zod' to generate schemas\n");
7665
- console.log("Things just got Cerios...\n");
8021
+ const ceriosMessages = [
8022
+ "Things just got Cerios!",
8023
+ "Getting Cerios about schemas!",
8024
+ "Cerios business ahead!",
8025
+ "Don't take it too Cerios-ly!",
8026
+ "Time to get Cerios!",
8027
+ "We're dead Cerios about types!",
8028
+ "This is Cerios-ly awesome!",
8029
+ "Cerios-ly, you're all set!",
8030
+ "You are Cerios right now!",
8031
+ "Cerios vibes only!"
8032
+ ];
8033
+ const randomMessage = ceriosMessages[Math.floor(Math.random() * ceriosMessages.length)];
8034
+ console.log(`${randomMessage}
8035
+ `);
7666
8036
  }
7667
8037
  }
7668
8038
  });