@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/index.mjs CHANGED
@@ -1252,6 +1252,140 @@ _PropertyGenerator.INCLUSION_RULES = {
1252
1252
  };
1253
1253
  var PropertyGenerator = _PropertyGenerator;
1254
1254
 
1255
+ // src/utils/operation-filters.ts
1256
+ import { minimatch } from "minimatch";
1257
+ function createFilterStatistics() {
1258
+ return {
1259
+ totalOperations: 0,
1260
+ includedOperations: 0,
1261
+ filteredByTags: 0,
1262
+ filteredByPaths: 0,
1263
+ filteredByMethods: 0,
1264
+ filteredByOperationIds: 0,
1265
+ filteredByDeprecated: 0
1266
+ };
1267
+ }
1268
+ function matchesAnyPattern(value, patterns) {
1269
+ if (!patterns || patterns.length === 0) {
1270
+ return false;
1271
+ }
1272
+ if (!value) {
1273
+ return false;
1274
+ }
1275
+ return patterns.some((pattern) => minimatch(value, pattern));
1276
+ }
1277
+ function containsAny(arr, values) {
1278
+ if (!values || values.length === 0) {
1279
+ return false;
1280
+ }
1281
+ if (!arr || arr.length === 0) {
1282
+ return false;
1283
+ }
1284
+ return values.some((value) => arr.includes(value));
1285
+ }
1286
+ function shouldIncludeOperation(operation, path, method, filters, stats) {
1287
+ if (!filters) {
1288
+ return true;
1289
+ }
1290
+ const methodLower = method.toLowerCase();
1291
+ const operationId = operation == null ? void 0 : operation.operationId;
1292
+ const tags = (operation == null ? void 0 : operation.tags) || [];
1293
+ const deprecated = (operation == null ? void 0 : operation.deprecated) === true;
1294
+ if (filters.includeTags && filters.includeTags.length > 0) {
1295
+ if (!containsAny(tags, filters.includeTags)) {
1296
+ if (stats) stats.filteredByTags++;
1297
+ return false;
1298
+ }
1299
+ }
1300
+ if (filters.includePaths && filters.includePaths.length > 0) {
1301
+ if (!matchesAnyPattern(path, filters.includePaths)) {
1302
+ if (stats) stats.filteredByPaths++;
1303
+ return false;
1304
+ }
1305
+ }
1306
+ if (filters.includeMethods && filters.includeMethods.length > 0) {
1307
+ const methodsLower = filters.includeMethods.map((m) => m.toLowerCase());
1308
+ if (!methodsLower.includes(methodLower)) {
1309
+ if (stats) stats.filteredByMethods++;
1310
+ return false;
1311
+ }
1312
+ }
1313
+ if (filters.includeOperationIds && filters.includeOperationIds.length > 0) {
1314
+ if (!matchesAnyPattern(operationId, filters.includeOperationIds)) {
1315
+ if (stats) stats.filteredByOperationIds++;
1316
+ return false;
1317
+ }
1318
+ }
1319
+ if (filters.excludeDeprecated === true && deprecated) {
1320
+ if (stats) stats.filteredByDeprecated++;
1321
+ return false;
1322
+ }
1323
+ if (filters.excludeTags && filters.excludeTags.length > 0) {
1324
+ if (containsAny(tags, filters.excludeTags)) {
1325
+ if (stats) stats.filteredByTags++;
1326
+ return false;
1327
+ }
1328
+ }
1329
+ if (filters.excludePaths && filters.excludePaths.length > 0) {
1330
+ if (matchesAnyPattern(path, filters.excludePaths)) {
1331
+ if (stats) stats.filteredByPaths++;
1332
+ return false;
1333
+ }
1334
+ }
1335
+ if (filters.excludeMethods && filters.excludeMethods.length > 0) {
1336
+ const methodsLower = filters.excludeMethods.map((m) => m.toLowerCase());
1337
+ if (methodsLower.includes(methodLower)) {
1338
+ if (stats) stats.filteredByMethods++;
1339
+ return false;
1340
+ }
1341
+ }
1342
+ if (filters.excludeOperationIds && filters.excludeOperationIds.length > 0) {
1343
+ if (matchesAnyPattern(operationId, filters.excludeOperationIds)) {
1344
+ if (stats) stats.filteredByOperationIds++;
1345
+ return false;
1346
+ }
1347
+ }
1348
+ return true;
1349
+ }
1350
+ function validateFilters(stats, filters) {
1351
+ if (!filters || stats.totalOperations === 0) {
1352
+ return;
1353
+ }
1354
+ if (stats.includedOperations === 0) {
1355
+ console.warn(
1356
+ `\u26A0\uFE0F Warning: All ${stats.totalOperations} operations were filtered out. Check your operationFilters configuration.`
1357
+ );
1358
+ const filterBreakdown = [];
1359
+ if (stats.filteredByTags > 0) filterBreakdown.push(`${stats.filteredByTags} by tags`);
1360
+ if (stats.filteredByPaths > 0) filterBreakdown.push(`${stats.filteredByPaths} by paths`);
1361
+ if (stats.filteredByMethods > 0) filterBreakdown.push(`${stats.filteredByMethods} by methods`);
1362
+ if (stats.filteredByOperationIds > 0) filterBreakdown.push(`${stats.filteredByOperationIds} by operationIds`);
1363
+ if (stats.filteredByDeprecated > 0) filterBreakdown.push(`${stats.filteredByDeprecated} by deprecated flag`);
1364
+ if (filterBreakdown.length > 0) {
1365
+ console.warn(` Filtered: ${filterBreakdown.join(", ")}`);
1366
+ }
1367
+ }
1368
+ }
1369
+ function formatFilterStatistics(stats) {
1370
+ if (stats.totalOperations === 0) {
1371
+ return "";
1372
+ }
1373
+ const lines = [];
1374
+ lines.push("Operation Filtering:");
1375
+ lines.push(` Total operations: ${stats.totalOperations}`);
1376
+ lines.push(` Included operations: ${stats.includedOperations}`);
1377
+ const filteredCount = stats.filteredByTags + stats.filteredByPaths + stats.filteredByMethods + stats.filteredByOperationIds + stats.filteredByDeprecated;
1378
+ if (filteredCount > 0) {
1379
+ lines.push(` Filtered operations: ${filteredCount}`);
1380
+ if (stats.filteredByTags > 0) lines.push(` - By tags: ${stats.filteredByTags}`);
1381
+ if (stats.filteredByPaths > 0) lines.push(` - By paths: ${stats.filteredByPaths}`);
1382
+ if (stats.filteredByMethods > 0) lines.push(` - By methods: ${stats.filteredByMethods}`);
1383
+ if (stats.filteredByOperationIds > 0) lines.push(` - By operationIds: ${stats.filteredByOperationIds}`);
1384
+ if (stats.filteredByDeprecated > 0) lines.push(` - By deprecated: ${stats.filteredByDeprecated}`);
1385
+ }
1386
+ return lines.join("\n");
1387
+ }
1388
+
1255
1389
  // src/openapi-generator.ts
1256
1390
  var OpenApiGenerator = class {
1257
1391
  constructor(options) {
@@ -1263,6 +1397,7 @@ var OpenApiGenerator = class {
1263
1397
  this.schemaUsageMap = /* @__PURE__ */ new Map();
1264
1398
  this.schemaTypeModeMap = /* @__PURE__ */ new Map();
1265
1399
  this.needsZodImport = false;
1400
+ this.filterStats = createFilterStatistics();
1266
1401
  var _a, _b, _c;
1267
1402
  if (!options.input) {
1268
1403
  throw new ConfigurationError("Input path is required", { providedOptions: options });
@@ -1280,7 +1415,8 @@ var OpenApiGenerator = class {
1280
1415
  showStats: (_c = options.showStats) != null ? _c : true,
1281
1416
  nativeEnumType: options.nativeEnumType || "union",
1282
1417
  request: options.request,
1283
- response: options.response
1418
+ response: options.response,
1419
+ operationFilters: options.operationFilters
1284
1420
  };
1285
1421
  try {
1286
1422
  const fs = __require("fs");
@@ -1385,6 +1521,8 @@ var OpenApiGenerator = class {
1385
1521
  this.generateComponentSchema(name, schema);
1386
1522
  }
1387
1523
  this.generateQueryParameterSchemas();
1524
+ this.generateHeaderParameterSchemas();
1525
+ validateFilters(this.filterStats, this.options.operationFilters);
1388
1526
  const orderedSchemaNames = this.topologicalSort();
1389
1527
  const output = ["// Auto-generated by @cerios/openapi-to-zod", "// Do not edit this file manually", ""];
1390
1528
  if (this.options.showStats === true) {
@@ -1478,9 +1616,16 @@ var OpenApiGenerator = class {
1478
1616
  const requestSchemas = /* @__PURE__ */ new Set();
1479
1617
  const responseSchemas = /* @__PURE__ */ new Set();
1480
1618
  if (this.spec.paths) {
1481
- for (const [, pathItem] of Object.entries(this.spec.paths)) {
1482
- for (const [, operation] of Object.entries(pathItem)) {
1619
+ for (const [path, pathItem] of Object.entries(this.spec.paths)) {
1620
+ const methods = ["get", "post", "put", "patch", "delete", "head", "options"];
1621
+ for (const method of methods) {
1622
+ const operation = pathItem[method];
1483
1623
  if (typeof operation !== "object" || !operation) continue;
1624
+ this.filterStats.totalOperations++;
1625
+ if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters, this.filterStats)) {
1626
+ continue;
1627
+ }
1628
+ this.filterStats.includedOperations++;
1484
1629
  if ("requestBody" in operation && operation.requestBody && typeof operation.requestBody === "object" && "content" in operation.requestBody && operation.requestBody.content) {
1485
1630
  for (const mediaType of Object.values(operation.requestBody.content)) {
1486
1631
  if (mediaType && typeof mediaType === "object" && "schema" in mediaType && mediaType.schema) {
@@ -1830,12 +1975,15 @@ ${typeCode}`;
1830
1975
  if (!this.spec.paths) {
1831
1976
  return;
1832
1977
  }
1833
- for (const [_path, pathItem] of Object.entries(this.spec.paths)) {
1978
+ for (const [path, pathItem] of Object.entries(this.spec.paths)) {
1834
1979
  if (!pathItem || typeof pathItem !== "object") continue;
1835
1980
  const methods = ["get", "post", "put", "patch", "delete", "head", "options"];
1836
1981
  for (const method of methods) {
1837
1982
  const operation = pathItem[method];
1838
1983
  if (!operation) continue;
1984
+ if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
1985
+ continue;
1986
+ }
1839
1987
  if (!operation.operationId || !operation.parameters || !Array.isArray(operation.parameters)) {
1840
1988
  continue;
1841
1989
  }
@@ -1896,6 +2044,80 @@ ${propsCode}
1896
2044
  const jsdoc = `/**
1897
2045
  * Query parameters for ${operation.operationId}
1898
2046
  */
2047
+ `;
2048
+ const fullSchemaCode = `${jsdoc}export const ${camelCaseSchemaName} = ${schemaCode};`;
2049
+ this.schemas.set(schemaName, fullSchemaCode);
2050
+ this.needsZodImport = true;
2051
+ }
2052
+ }
2053
+ }
2054
+ /**
2055
+ * Generate header parameter schemas for each operation
2056
+ * Header parameters are always string type (HTTP header semantics)
2057
+ */
2058
+ generateHeaderParameterSchemas() {
2059
+ var _a;
2060
+ if (!this.spec.paths) {
2061
+ return;
2062
+ }
2063
+ for (const [path, pathItem] of Object.entries(this.spec.paths)) {
2064
+ if (!pathItem || typeof pathItem !== "object") continue;
2065
+ const methods = ["get", "post", "put", "patch", "delete", "head", "options"];
2066
+ for (const method of methods) {
2067
+ const operation = pathItem[method];
2068
+ if (!operation) continue;
2069
+ if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
2070
+ continue;
2071
+ }
2072
+ if (!operation.operationId || !operation.parameters || !Array.isArray(operation.parameters)) {
2073
+ continue;
2074
+ }
2075
+ const headerParams = operation.parameters.filter(
2076
+ (param) => param && typeof param === "object" && param.in === "header"
2077
+ );
2078
+ if (headerParams.length === 0) {
2079
+ continue;
2080
+ }
2081
+ const pascalOperationId = operation.operationId.includes("-") ? toPascalCase(operation.operationId) : operation.operationId.charAt(0).toUpperCase() + operation.operationId.slice(1);
2082
+ const schemaName = `${pascalOperationId}HeaderParams`;
2083
+ if (!this.schemaDependencies.has(schemaName)) {
2084
+ this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
2085
+ }
2086
+ const properties = {};
2087
+ for (const param of headerParams) {
2088
+ const paramName = param.name;
2089
+ const paramSchema = param.schema;
2090
+ if (!paramSchema) continue;
2091
+ let zodType = "z.string()";
2092
+ if (param.description && this.requestOptions.includeDescriptions) {
2093
+ if (this.requestOptions.useDescribe) {
2094
+ zodType = `${zodType}.describe(${JSON.stringify(param.description)})`;
2095
+ }
2096
+ }
2097
+ zodType = `${zodType}.optional()`;
2098
+ properties[paramName] = zodType;
2099
+ if (paramSchema.$ref) {
2100
+ const refName = resolveRef(paramSchema.$ref);
2101
+ (_a = this.schemaDependencies.get(schemaName)) == null ? void 0 : _a.add(refName);
2102
+ }
2103
+ }
2104
+ const objectMode = this.requestOptions.mode;
2105
+ const zodMethod = objectMode === "strict" ? "strictObject" : objectMode === "loose" ? "looseObject" : "object";
2106
+ const propsCode = Object.entries(properties).map(([key, value]) => {
2107
+ const needsQuotes = !/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key);
2108
+ const quotedKey = needsQuotes ? `"${key}"` : key;
2109
+ return ` ${quotedKey}: ${value}`;
2110
+ }).join(",\n");
2111
+ const schemaCode = `z.${zodMethod}({
2112
+ ${propsCode}
2113
+ })`;
2114
+ const operationName = pascalOperationId;
2115
+ const prefixedName = this.options.prefix ? `${toPascalCase(this.options.prefix)}${operationName}` : operationName;
2116
+ const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
2117
+ const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}HeaderParamsSchema`;
2118
+ const jsdoc = `/**
2119
+ * Header parameters for ${operation.operationId}
2120
+ */
1899
2121
  `;
1900
2122
  const fullSchemaCode = `${jsdoc}export const ${camelCaseSchemaName} = ${schemaCode};`;
1901
2123
  this.schemas.set(schemaName, fullSchemaCode);
@@ -2173,15 +2395,23 @@ ${props.join("\n")}
2173
2395
  stats.withConstraints++;
2174
2396
  }
2175
2397
  }
2176
- return [
2398
+ const output = [
2177
2399
  "// Generation Statistics:",
2178
2400
  `// Total schemas: ${stats.totalSchemas}`,
2179
2401
  `// Enums: ${stats.enums}`,
2180
2402
  `// Circular references: ${stats.withCircularRefs}`,
2181
2403
  `// Discriminated unions: ${stats.withDiscriminators}`,
2182
- `// With constraints: ${stats.withConstraints}`,
2183
- `// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`
2404
+ `// With constraints: ${stats.withConstraints}`
2184
2405
  ];
2406
+ if (this.options.operationFilters && this.filterStats.totalOperations > 0) {
2407
+ output.push("//");
2408
+ const filterStatsStr = formatFilterStatistics(this.filterStats);
2409
+ for (const line of filterStatsStr.split("\n")) {
2410
+ output.push(`// ${line}`);
2411
+ }
2412
+ }
2413
+ output.push(`// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`);
2414
+ return output;
2185
2415
  }
2186
2416
  };
2187
2417
 
@@ -2198,6 +2428,10 @@ export {
2198
2428
  OpenApiGenerator,
2199
2429
  SchemaGenerationError,
2200
2430
  SpecValidationError,
2201
- defineConfig
2431
+ createFilterStatistics,
2432
+ defineConfig,
2433
+ formatFilterStatistics,
2434
+ shouldIncludeOperation,
2435
+ validateFilters
2202
2436
  };
2203
2437
  //# sourceMappingURL=index.mjs.map