@cerios/openapi-to-zod 0.4.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,6 +6291,146 @@ var init_property_generator = __esm({
6291
6291
  }
6292
6292
  });
6293
6293
 
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
+
6294
6434
  // src/openapi-generator.ts
6295
6435
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
6296
6436
  import { dirname, normalize } from "path";
@@ -6305,6 +6445,7 @@ var init_openapi_generator = __esm({
6305
6445
  init_jsdoc_generator();
6306
6446
  init_property_generator();
6307
6447
  init_name_utils();
6448
+ init_operation_filters();
6308
6449
  OpenApiGenerator = class {
6309
6450
  constructor(options) {
6310
6451
  this.schemas = /* @__PURE__ */ new Map();
@@ -6315,6 +6456,7 @@ var init_openapi_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_openapi_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");
@@ -6437,6 +6580,8 @@ var init_openapi_generator = __esm({
6437
6580
  this.generateComponentSchema(name, schema);
6438
6581
  }
6439
6582
  this.generateQueryParameterSchemas();
6583
+ this.generateHeaderParameterSchemas();
6584
+ validateFilters(this.filterStats, this.options.operationFilters);
6440
6585
  const orderedSchemaNames = this.topologicalSort();
6441
6586
  const output = ["// Auto-generated by @cerios/openapi-to-zod", "// Do not edit this file manually", ""];
6442
6587
  if (this.options.showStats === true) {
@@ -6530,9 +6675,16 @@ var init_openapi_generator = __esm({
6530
6675
  const requestSchemas = /* @__PURE__ */ new Set();
6531
6676
  const responseSchemas = /* @__PURE__ */ new Set();
6532
6677
  if (this.spec.paths) {
6533
- for (const [, pathItem] of Object.entries(this.spec.paths)) {
6534
- 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];
6535
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++;
6536
6688
  if ("requestBody" in operation && operation.requestBody && typeof operation.requestBody === "object" && "content" in operation.requestBody && operation.requestBody.content) {
6537
6689
  for (const mediaType of Object.values(operation.requestBody.content)) {
6538
6690
  if (mediaType && typeof mediaType === "object" && "schema" in mediaType && mediaType.schema) {
@@ -6882,12 +7034,15 @@ ${typeCode}`;
6882
7034
  if (!this.spec.paths) {
6883
7035
  return;
6884
7036
  }
6885
- for (const [_path, pathItem] of Object.entries(this.spec.paths)) {
7037
+ for (const [path2, pathItem] of Object.entries(this.spec.paths)) {
6886
7038
  if (!pathItem || typeof pathItem !== "object") continue;
6887
7039
  const methods = ["get", "post", "put", "patch", "delete", "head", "options"];
6888
7040
  for (const method of methods) {
6889
7041
  const operation = pathItem[method];
6890
7042
  if (!operation) continue;
7043
+ if (!shouldIncludeOperation(operation, path2, method, this.options.operationFilters)) {
7044
+ continue;
7045
+ }
6891
7046
  if (!operation.operationId || !operation.parameters || !Array.isArray(operation.parameters)) {
6892
7047
  continue;
6893
7048
  }
@@ -6948,6 +7103,80 @@ ${propsCode}
6948
7103
  const jsdoc = `/**
6949
7104
  * Query parameters for ${operation.operationId}
6950
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
+ */
6951
7180
  `;
6952
7181
  const fullSchemaCode = `${jsdoc}export const ${camelCaseSchemaName} = ${schemaCode};`;
6953
7182
  this.schemas.set(schemaName, fullSchemaCode);
@@ -7225,15 +7454,23 @@ ${props.join("\n")}
7225
7454
  stats.withConstraints++;
7226
7455
  }
7227
7456
  }
7228
- return [
7457
+ const output = [
7229
7458
  "// Generation Statistics:",
7230
7459
  `// Total schemas: ${stats.totalSchemas}`,
7231
7460
  `// Enums: ${stats.enums}`,
7232
7461
  `// Circular references: ${stats.withCircularRefs}`,
7233
7462
  `// Discriminated unions: ${stats.withDiscriminators}`,
7234
- `// With constraints: ${stats.withConstraints}`,
7235
- `// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`
7463
+ `// With constraints: ${stats.withConstraints}`
7236
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;
7237
7474
  }
7238
7475
  };
7239
7476
  }
@@ -7419,7 +7656,7 @@ function mergeConfigWithDefaults(config) {
7419
7656
  return merged;
7420
7657
  });
7421
7658
  }
7422
- var TypeModeSchema, NativeEnumTypeSchema, RequestResponseOptionsSchema, OpenApiGeneratorOptionsSchema, ConfigFileSchema, createTypeScriptLoader;
7659
+ var TypeModeSchema, NativeEnumTypeSchema, RequestResponseOptionsSchema, OperationFiltersSchema, OpenApiGeneratorOptionsSchema, ConfigFileSchema, createTypeScriptLoader;
7423
7660
  var init_config_loader = __esm({
7424
7661
  "src/utils/config-loader.ts"() {
7425
7662
  "use strict";
@@ -7434,6 +7671,17 @@ var init_config_loader = __esm({
7434
7671
  typeMode: TypeModeSchema.optional(),
7435
7672
  nativeEnumType: NativeEnumTypeSchema.optional()
7436
7673
  });
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
+ });
7437
7685
  OpenApiGeneratorOptionsSchema = z.strictObject({
7438
7686
  mode: z.enum(["strict", "normal", "loose"]).optional(),
7439
7687
  input: z.string(),
@@ -7448,7 +7696,8 @@ var init_config_loader = __esm({
7448
7696
  nativeEnumType: NativeEnumTypeSchema.optional(),
7449
7697
  request: RequestResponseOptionsSchema.optional(),
7450
7698
  response: RequestResponseOptionsSchema.optional(),
7451
- name: z.string().optional()
7699
+ name: z.string().optional(),
7700
+ operationFilters: OperationFiltersSchema.optional()
7452
7701
  });
7453
7702
  ConfigFileSchema = z.strictObject({
7454
7703
  defaults: z.strictObject({
@@ -7462,7 +7711,8 @@ var init_config_loader = __esm({
7462
7711
  showStats: z.boolean().optional(),
7463
7712
  nativeEnumType: NativeEnumTypeSchema.optional(),
7464
7713
  request: RequestResponseOptionsSchema.optional(),
7465
- response: RequestResponseOptionsSchema.optional()
7714
+ response: RequestResponseOptionsSchema.optional(),
7715
+ operationFilters: OperationFiltersSchema.optional()
7466
7716
  }).optional(),
7467
7717
  specs: z.array(OpenApiGeneratorOptionsSchema).min(1, "At least one spec is required"),
7468
7718
  executionMode: z.enum(["parallel", "sequential"]).optional()
@@ -7768,7 +8018,21 @@ export default defineConfig({
7768
8018
  console.log("\nNext steps:");
7769
8019
  console.log(" 1. Review and customize your config file if needed");
7770
8020
  console.log(" 2. Run 'openapi-to-zod' to generate schemas\n");
7771
- 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
+ `);
7772
8036
  }
7773
8037
  }
7774
8038
  });