@cerios/openapi-to-zod 0.6.0 → 1.1.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 +448 -51
- package/dist/cli.js +355 -186
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +418 -224
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +9 -344
- package/dist/index.d.ts +9 -344
- package/dist/index.js +156 -27
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +155 -22
- package/dist/index.mjs.map +1 -1
- package/dist/internal.d.mts +257 -0
- package/dist/internal.d.ts +257 -0
- package/dist/internal.js +592 -0
- package/dist/internal.js.map +1 -0
- package/dist/internal.mjs +547 -0
- package/dist/internal.mjs.map +1 -0
- package/dist/types-B7ePTDjr.d.mts +345 -0
- package/dist/types-B7ePTDjr.d.ts +345 -0
- package/package.json +102 -78
package/dist/index.mjs
CHANGED
|
@@ -67,11 +67,24 @@ var ConfigurationError = class extends GeneratorError {
|
|
|
67
67
|
// src/openapi-generator.ts
|
|
68
68
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
69
69
|
import { dirname, normalize } from "path";
|
|
70
|
+
import { minimatch as minimatch2 } from "minimatch";
|
|
70
71
|
import { parse } from "yaml";
|
|
71
72
|
|
|
72
73
|
// src/utils/name-utils.ts
|
|
74
|
+
function sanitizeIdentifier(str) {
|
|
75
|
+
return str.replace(/[^a-zA-Z0-9._\-\s]+/g, "_");
|
|
76
|
+
}
|
|
73
77
|
function toCamelCase(str, options) {
|
|
74
|
-
|
|
78
|
+
const sanitized = sanitizeIdentifier(str);
|
|
79
|
+
const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
|
|
80
|
+
let name;
|
|
81
|
+
if (words.length === 0) {
|
|
82
|
+
name = str.charAt(0).toLowerCase() + str.slice(1);
|
|
83
|
+
} else if (words.length === 1) {
|
|
84
|
+
name = words[0].charAt(0).toLowerCase() + words[0].slice(1);
|
|
85
|
+
} else {
|
|
86
|
+
name = words[0].charAt(0).toLowerCase() + words[0].slice(1) + words.slice(1).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
87
|
+
}
|
|
75
88
|
if (options == null ? void 0 : options.prefix) {
|
|
76
89
|
const prefix = options.prefix.toLowerCase();
|
|
77
90
|
name = prefix + name.charAt(0).toUpperCase() + name.slice(1);
|
|
@@ -84,12 +97,23 @@ function toCamelCase(str, options) {
|
|
|
84
97
|
}
|
|
85
98
|
function toPascalCase(str) {
|
|
86
99
|
const stringValue = String(str);
|
|
87
|
-
|
|
100
|
+
const isAlreadyValidCase = /^[a-zA-Z][a-zA-Z0-9]*$/.test(stringValue);
|
|
101
|
+
if (isAlreadyValidCase) {
|
|
102
|
+
return stringValue.charAt(0).toUpperCase() + stringValue.slice(1);
|
|
103
|
+
}
|
|
104
|
+
const sanitized = sanitizeIdentifier(stringValue);
|
|
105
|
+
const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
|
|
106
|
+
let result;
|
|
107
|
+
if (words.length === 0) {
|
|
108
|
+
result = "Value";
|
|
109
|
+
} else {
|
|
110
|
+
result = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
111
|
+
}
|
|
88
112
|
if (/^\d/.test(result)) {
|
|
89
113
|
result = `N${result}`;
|
|
90
114
|
}
|
|
91
115
|
if (!result || /^_+$/.test(result)) {
|
|
92
|
-
|
|
116
|
+
return "Value";
|
|
93
117
|
}
|
|
94
118
|
return result;
|
|
95
119
|
}
|
|
@@ -101,9 +125,10 @@ function resolveRef(ref) {
|
|
|
101
125
|
// src/generators/enum-generator.ts
|
|
102
126
|
function generateEnum(name, values, options) {
|
|
103
127
|
const schemaName = `${toCamelCase(name, options)}Schema`;
|
|
128
|
+
const typeName = toPascalCase(name);
|
|
104
129
|
const enumValues = values.map((v) => `"${v}"`).join(", ");
|
|
105
130
|
const schemaCode = `export const ${schemaName} = z.enum([${enumValues}]);`;
|
|
106
|
-
const typeCode = `export type ${
|
|
131
|
+
const typeCode = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
|
|
107
132
|
return { schemaCode, typeCode };
|
|
108
133
|
}
|
|
109
134
|
|
|
@@ -203,6 +228,9 @@ var LRUCache = class {
|
|
|
203
228
|
this.cache = /* @__PURE__ */ new Map();
|
|
204
229
|
this.maxSize = maxSize;
|
|
205
230
|
}
|
|
231
|
+
get capacity() {
|
|
232
|
+
return this.maxSize;
|
|
233
|
+
}
|
|
206
234
|
get(key) {
|
|
207
235
|
if (!this.cache.has(key)) return void 0;
|
|
208
236
|
const value = this.cache.get(key);
|
|
@@ -233,6 +261,76 @@ var LRUCache = class {
|
|
|
233
261
|
}
|
|
234
262
|
};
|
|
235
263
|
|
|
264
|
+
// src/utils/pattern-utils.ts
|
|
265
|
+
function isRegexPattern(pattern) {
|
|
266
|
+
if (pattern.startsWith("^") || pattern.endsWith("$")) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
if (/\\[dDwWsS]/.test(pattern)) {
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
if (/\.\*|\.\+/.test(pattern)) {
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
if (/[[\]()]/.test(pattern)) {
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
if (/[^/][+?*]\{/.test(pattern)) {
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
function patternToRegex(pattern) {
|
|
284
|
+
if (pattern instanceof RegExp) {
|
|
285
|
+
return pattern;
|
|
286
|
+
}
|
|
287
|
+
if (isRegexPattern(pattern)) {
|
|
288
|
+
try {
|
|
289
|
+
return new RegExp(pattern);
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.warn(`\u26A0\uFE0F Invalid regex pattern "${pattern}": ${error instanceof Error ? error.message : String(error)}`);
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
function stripPrefix(input, pattern, ensureLeadingChar) {
|
|
298
|
+
if (!pattern) {
|
|
299
|
+
return input;
|
|
300
|
+
}
|
|
301
|
+
const regex = patternToRegex(pattern);
|
|
302
|
+
if (regex) {
|
|
303
|
+
const match = input.match(regex);
|
|
304
|
+
if (match && match.index === 0) {
|
|
305
|
+
const stripped = input.substring(match[0].length);
|
|
306
|
+
if (ensureLeadingChar) {
|
|
307
|
+
if (stripped === "") {
|
|
308
|
+
return ensureLeadingChar;
|
|
309
|
+
}
|
|
310
|
+
if (!stripped.startsWith(ensureLeadingChar)) {
|
|
311
|
+
return `${ensureLeadingChar}${stripped}`;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return stripped;
|
|
315
|
+
}
|
|
316
|
+
} else {
|
|
317
|
+
const stringPattern = pattern;
|
|
318
|
+
if (input.startsWith(stringPattern)) {
|
|
319
|
+
const stripped = input.substring(stringPattern.length);
|
|
320
|
+
if (ensureLeadingChar) {
|
|
321
|
+
if (stripped === "") {
|
|
322
|
+
return ensureLeadingChar;
|
|
323
|
+
}
|
|
324
|
+
if (!stripped.startsWith(ensureLeadingChar)) {
|
|
325
|
+
return `${ensureLeadingChar}${stripped}`;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
return stripped;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return input;
|
|
332
|
+
}
|
|
333
|
+
|
|
236
334
|
// src/validators/array-validator.ts
|
|
237
335
|
function generateArrayValidation(schema, context) {
|
|
238
336
|
var _a;
|
|
@@ -752,6 +850,11 @@ ${properties.join(",\n")}
|
|
|
752
850
|
|
|
753
851
|
// src/validators/string-validator.ts
|
|
754
852
|
var PATTERN_CACHE = new LRUCache(1e3);
|
|
853
|
+
function configurePatternCache(size) {
|
|
854
|
+
if (size > 0 && size !== PATTERN_CACHE.capacity) {
|
|
855
|
+
PATTERN_CACHE = new LRUCache(size);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
755
858
|
var FORMAT_MAP = {
|
|
756
859
|
uuid: "z.uuid()",
|
|
757
860
|
email: "z.email()",
|
|
@@ -1084,8 +1187,9 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
1084
1187
|
}
|
|
1085
1188
|
(_a = this.context.schemaDependencies.get(currentSchema)) == null ? void 0 : _a.add(refName);
|
|
1086
1189
|
}
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1190
|
+
const strippedRefName = stripPrefix(resolvedRefName, this.context.stripSchemaPrefix);
|
|
1191
|
+
const schemaName = `${toCamelCase(strippedRefName, this.context.namingOptions)}Schema`;
|
|
1192
|
+
if (currentSchema && (refName === currentSchema || this.isCircularThroughAlias(currentSchema, refName))) {
|
|
1089
1193
|
const lazySchema = `z.lazy((): z.ZodTypeAny => ${schemaName})`;
|
|
1090
1194
|
return wrapNullable(lazySchema, nullable);
|
|
1091
1195
|
}
|
|
@@ -1372,7 +1476,7 @@ var OpenApiGenerator = class {
|
|
|
1372
1476
|
this.schemaUsageMap = /* @__PURE__ */ new Map();
|
|
1373
1477
|
this.needsZodImport = true;
|
|
1374
1478
|
this.filterStats = createFilterStatistics();
|
|
1375
|
-
var _a, _b, _c;
|
|
1479
|
+
var _a, _b, _c, _d, _e;
|
|
1376
1480
|
if (!options.input) {
|
|
1377
1481
|
throw new ConfigurationError("Input path is required", { providedOptions: options });
|
|
1378
1482
|
}
|
|
@@ -1385,11 +1489,18 @@ var OpenApiGenerator = class {
|
|
|
1385
1489
|
schemaType: options.schemaType || "all",
|
|
1386
1490
|
prefix: options.prefix,
|
|
1387
1491
|
suffix: options.suffix,
|
|
1492
|
+
stripSchemaPrefix: options.stripSchemaPrefix,
|
|
1388
1493
|
showStats: (_c = options.showStats) != null ? _c : true,
|
|
1389
1494
|
request: options.request,
|
|
1390
1495
|
response: options.response,
|
|
1391
|
-
operationFilters: options.operationFilters
|
|
1496
|
+
operationFilters: options.operationFilters,
|
|
1497
|
+
ignoreHeaders: options.ignoreHeaders,
|
|
1498
|
+
cacheSize: (_d = options.cacheSize) != null ? _d : 1e3,
|
|
1499
|
+
batchSize: (_e = options.batchSize) != null ? _e : 10
|
|
1392
1500
|
};
|
|
1501
|
+
if (this.options.cacheSize) {
|
|
1502
|
+
configurePatternCache(this.options.cacheSize);
|
|
1503
|
+
}
|
|
1393
1504
|
try {
|
|
1394
1505
|
const fs = __require("fs");
|
|
1395
1506
|
if (!fs.existsSync(this.options.input)) {
|
|
@@ -1455,7 +1566,8 @@ var OpenApiGenerator = class {
|
|
|
1455
1566
|
namingOptions: {
|
|
1456
1567
|
prefix: this.options.prefix,
|
|
1457
1568
|
suffix: this.options.suffix
|
|
1458
|
-
}
|
|
1569
|
+
},
|
|
1570
|
+
stripSchemaPrefix: this.options.stripSchemaPrefix
|
|
1459
1571
|
});
|
|
1460
1572
|
}
|
|
1461
1573
|
/**
|
|
@@ -1489,9 +1601,11 @@ var OpenApiGenerator = class {
|
|
|
1489
1601
|
const typeCode = this.types.get(name);
|
|
1490
1602
|
if (schemaCode) {
|
|
1491
1603
|
output.push(schemaCode);
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1604
|
+
const strippedName = stripPrefix(name, this.options.stripSchemaPrefix);
|
|
1605
|
+
const typeName = toPascalCase(strippedName);
|
|
1606
|
+
if (!schemaCode.includes(`export type ${typeName}`)) {
|
|
1607
|
+
const schemaName = `${toCamelCase(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
|
|
1608
|
+
output.push(`export type ${typeName} = z.infer<typeof ${schemaName}>;`);
|
|
1495
1609
|
}
|
|
1496
1610
|
output.push("");
|
|
1497
1611
|
} else if (typeCode) {
|
|
@@ -1525,6 +1639,7 @@ var OpenApiGenerator = class {
|
|
|
1525
1639
|
const normalizedOutput = normalize(this.options.output);
|
|
1526
1640
|
this.ensureDirectoryExists(normalizedOutput);
|
|
1527
1641
|
writeFileSync(normalizedOutput, output);
|
|
1642
|
+
console.log(` \u2713 Generated ${normalizedOutput}`);
|
|
1528
1643
|
}
|
|
1529
1644
|
/**
|
|
1530
1645
|
* Resolve options for a specific context (request or response)
|
|
@@ -1806,7 +1921,8 @@ var OpenApiGenerator = class {
|
|
|
1806
1921
|
const resolvedOptions = context === "response" ? this.responseOptions : this.requestOptions;
|
|
1807
1922
|
if (schema.enum) {
|
|
1808
1923
|
const jsdoc2 = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
|
|
1809
|
-
const
|
|
1924
|
+
const strippedName2 = stripPrefix(name, this.options.stripSchemaPrefix);
|
|
1925
|
+
const { schemaCode, typeCode } = generateEnum(strippedName2, schema.enum, {
|
|
1810
1926
|
prefix: this.options.prefix,
|
|
1811
1927
|
suffix: this.options.suffix
|
|
1812
1928
|
});
|
|
@@ -1815,7 +1931,8 @@ ${typeCode}`;
|
|
|
1815
1931
|
this.schemas.set(name, enumSchemaCode);
|
|
1816
1932
|
return;
|
|
1817
1933
|
}
|
|
1818
|
-
const
|
|
1934
|
+
const strippedName = stripPrefix(name, this.options.stripSchemaPrefix);
|
|
1935
|
+
const schemaName = `${toCamelCase(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
|
|
1819
1936
|
const jsdoc = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
|
|
1820
1937
|
if (schema.allOf && schema.allOf.length === 1 && schema.allOf[0].$ref) {
|
|
1821
1938
|
const refName = resolveRef(schema.allOf[0].$ref);
|
|
@@ -1831,7 +1948,8 @@ ${typeCode}`;
|
|
|
1831
1948
|
namingOptions: {
|
|
1832
1949
|
prefix: this.options.prefix,
|
|
1833
1950
|
suffix: this.options.suffix
|
|
1834
|
-
}
|
|
1951
|
+
},
|
|
1952
|
+
stripSchemaPrefix: this.options.stripSchemaPrefix
|
|
1835
1953
|
});
|
|
1836
1954
|
const isAlias = !!(schema.$ref && !schema.properties && !schema.allOf && !schema.oneOf && !schema.anyOf);
|
|
1837
1955
|
const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, isAlias);
|
|
@@ -1935,6 +2053,24 @@ ${propsCode}
|
|
|
1935
2053
|
}
|
|
1936
2054
|
}
|
|
1937
2055
|
}
|
|
2056
|
+
/**
|
|
2057
|
+
* Check if a header should be ignored based on filter patterns
|
|
2058
|
+
* @internal
|
|
2059
|
+
*/
|
|
2060
|
+
shouldIgnoreHeader(headerName) {
|
|
2061
|
+
const ignorePatterns = this.options.ignoreHeaders;
|
|
2062
|
+
if (!ignorePatterns || ignorePatterns.length === 0) {
|
|
2063
|
+
return false;
|
|
2064
|
+
}
|
|
2065
|
+
if (ignorePatterns.includes("*")) {
|
|
2066
|
+
return true;
|
|
2067
|
+
}
|
|
2068
|
+
const headerLower = headerName.toLowerCase();
|
|
2069
|
+
return ignorePatterns.some((pattern) => {
|
|
2070
|
+
const patternLower = pattern.toLowerCase();
|
|
2071
|
+
return minimatch2(headerLower, patternLower);
|
|
2072
|
+
});
|
|
2073
|
+
}
|
|
1938
2074
|
/**
|
|
1939
2075
|
* Generate header parameter schemas for each operation
|
|
1940
2076
|
* Header parameters are always string type (HTTP header semantics)
|
|
@@ -1957,7 +2093,7 @@ ${propsCode}
|
|
|
1957
2093
|
continue;
|
|
1958
2094
|
}
|
|
1959
2095
|
const headerParams = operation.parameters.filter(
|
|
1960
|
-
(param) => param && typeof param === "object" && param.in === "header"
|
|
2096
|
+
(param) => param && typeof param === "object" && param.in === "header" && !this.shouldIgnoreHeader(param.name)
|
|
1961
2097
|
);
|
|
1962
2098
|
if (headerParams.length === 0) {
|
|
1963
2099
|
continue;
|
|
@@ -2015,7 +2151,8 @@ ${propsCode}
|
|
|
2015
2151
|
generateQueryParamType(schema, param) {
|
|
2016
2152
|
if (schema.$ref) {
|
|
2017
2153
|
const refName = resolveRef(schema.$ref);
|
|
2018
|
-
const
|
|
2154
|
+
const strippedRefName = stripPrefix(refName, this.options.stripSchemaPrefix);
|
|
2155
|
+
const schemaName = toCamelCase(strippedRefName, { prefix: this.options.prefix, suffix: this.options.suffix });
|
|
2019
2156
|
return `${schemaName}Schema`;
|
|
2020
2157
|
}
|
|
2021
2158
|
if (schema.enum) {
|
|
@@ -2167,10 +2304,6 @@ export {
|
|
|
2167
2304
|
OpenApiGenerator,
|
|
2168
2305
|
SchemaGenerationError,
|
|
2169
2306
|
SpecValidationError,
|
|
2170
|
-
|
|
2171
|
-
defineConfig,
|
|
2172
|
-
formatFilterStatistics,
|
|
2173
|
-
shouldIncludeOperation,
|
|
2174
|
-
validateFilters
|
|
2307
|
+
defineConfig
|
|
2175
2308
|
};
|
|
2176
2309
|
//# sourceMappingURL=index.mjs.map
|