@cerios/openapi-to-zod 1.3.2 → 1.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/LICENSE +8 -0
- package/README.md +546 -395
- package/dist/cli.js +831 -1321
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +864 -1371
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +472 -59
- package/dist/index.d.ts +472 -59
- package/dist/index.js +669 -909
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +675 -884
- package/dist/index.mjs.map +1 -1
- package/package.json +89 -104
- package/dist/internal.d.mts +0 -363
- package/dist/internal.d.ts +0 -363
- package/dist/internal.js +0 -759
- package/dist/internal.js.map +0 -1
- package/dist/internal.mjs +0 -706
- package/dist/internal.mjs.map +0 -1
- package/dist/types-DZ4Bw-D5.d.mts +0 -505
- package/dist/types-DZ4Bw-D5.d.ts +0 -505
package/dist/index.js
CHANGED
|
@@ -20,369 +20,35 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
-
CircularReferenceError: () => CircularReferenceError,
|
|
24
|
-
CliOptionsError: () => CliOptionsError,
|
|
25
|
-
ConfigValidationError: () => ConfigValidationError,
|
|
26
|
-
FileOperationError: () => FileOperationError,
|
|
27
|
-
GeneratorError: () => GeneratorError,
|
|
23
|
+
CircularReferenceError: () => import_openapi_core7.CircularReferenceError,
|
|
24
|
+
CliOptionsError: () => import_openapi_core7.CliOptionsError,
|
|
25
|
+
ConfigValidationError: () => import_openapi_core7.ConfigValidationError,
|
|
26
|
+
FileOperationError: () => import_openapi_core7.FileOperationError,
|
|
27
|
+
GeneratorError: () => import_openapi_core7.GeneratorError,
|
|
28
28
|
OpenApiGenerator: () => OpenApiGenerator,
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
PropertyGenerator: () => PropertyGenerator,
|
|
30
|
+
SchemaGenerationError: () => import_openapi_core7.SchemaGenerationError,
|
|
31
|
+
SpecValidationError: () => import_openapi_core7.SpecValidationError,
|
|
32
|
+
buildDateTimeValidation: () => buildDateTimeValidation,
|
|
31
33
|
defineConfig: () => defineConfig
|
|
32
34
|
});
|
|
33
35
|
module.exports = __toCommonJS(src_exports);
|
|
36
|
+
var import_openapi_core7 = require("@cerios/openapi-core");
|
|
34
37
|
|
|
35
|
-
// src/
|
|
36
|
-
var
|
|
37
|
-
constructor(message, code, context) {
|
|
38
|
-
var _a;
|
|
39
|
-
super(message);
|
|
40
|
-
this.code = code;
|
|
41
|
-
this.context = context;
|
|
42
|
-
this.name = "GeneratorError";
|
|
43
|
-
(_a = Error.captureStackTrace) == null ? void 0 : _a.call(Error, this, this.constructor);
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
var SpecValidationError = class extends GeneratorError {
|
|
47
|
-
constructor(message, context) {
|
|
48
|
-
super(message, "SPEC_VALIDATION_ERROR", context);
|
|
49
|
-
this.name = "SpecValidationError";
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
var FileOperationError = class extends GeneratorError {
|
|
53
|
-
constructor(message, filePath, context) {
|
|
54
|
-
super(message, "FILE_OPERATION_ERROR", { ...context, filePath });
|
|
55
|
-
this.filePath = filePath;
|
|
56
|
-
this.name = "FileOperationError";
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
var ConfigValidationError = class extends GeneratorError {
|
|
60
|
-
constructor(message, configPath, context) {
|
|
61
|
-
super(message, "CONFIG_VALIDATION_ERROR", { ...context, configPath });
|
|
62
|
-
this.configPath = configPath;
|
|
63
|
-
this.name = "ConfigValidationError";
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
var SchemaGenerationError = class extends GeneratorError {
|
|
67
|
-
constructor(message, schemaName, context) {
|
|
68
|
-
super(message, "SCHEMA_GENERATION_ERROR", { ...context, schemaName });
|
|
69
|
-
this.schemaName = schemaName;
|
|
70
|
-
this.name = "SchemaGenerationError";
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
var CircularReferenceError = class extends SchemaGenerationError {
|
|
74
|
-
constructor(schemaName, referencePath) {
|
|
75
|
-
const pathStr = referencePath.join(" -> ");
|
|
76
|
-
super(`Circular reference detected in schema: ${pathStr}`, schemaName, { referencePath, circularPath: pathStr });
|
|
77
|
-
this.referencePath = referencePath;
|
|
78
|
-
this.name = "CircularReferenceError";
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
var CliOptionsError = class extends GeneratorError {
|
|
82
|
-
constructor(message, context) {
|
|
83
|
-
super(message, "CLI_OPTIONS_ERROR", context);
|
|
84
|
-
this.name = "CliOptionsError";
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
var ConfigurationError = class extends GeneratorError {
|
|
88
|
-
constructor(message, context) {
|
|
89
|
-
super(message, "CONFIGURATION_ERROR", context);
|
|
90
|
-
this.name = "ConfigurationError";
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
// src/openapi-generator.ts
|
|
95
|
-
var import_node_fs = require("fs");
|
|
96
|
-
var import_node_path = require("path");
|
|
97
|
-
var import_minimatch3 = require("minimatch");
|
|
98
|
-
var import_yaml = require("yaml");
|
|
99
|
-
|
|
100
|
-
// src/utils/name-utils.ts
|
|
101
|
-
function sanitizeIdentifier(str) {
|
|
102
|
-
return str.replace(/[^a-zA-Z0-9._\-\s]+/g, "_");
|
|
103
|
-
}
|
|
104
|
-
function toCamelCase(str, options) {
|
|
105
|
-
const sanitized = sanitizeIdentifier(str);
|
|
106
|
-
const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
|
|
107
|
-
let name;
|
|
108
|
-
if (words.length === 0) {
|
|
109
|
-
name = str.charAt(0).toLowerCase() + str.slice(1);
|
|
110
|
-
} else if (words.length === 1) {
|
|
111
|
-
name = words[0].charAt(0).toLowerCase() + words[0].slice(1);
|
|
112
|
-
} else {
|
|
113
|
-
name = words[0].charAt(0).toLowerCase() + words[0].slice(1) + words.slice(1).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
114
|
-
}
|
|
115
|
-
if (options == null ? void 0 : options.prefix) {
|
|
116
|
-
const prefix = options.prefix.charAt(0).toLowerCase() + options.prefix.slice(1);
|
|
117
|
-
name = prefix + name.charAt(0).toUpperCase() + name.slice(1);
|
|
118
|
-
}
|
|
119
|
-
if (options == null ? void 0 : options.suffix) {
|
|
120
|
-
const suffix = options.suffix.charAt(0).toUpperCase() + options.suffix.slice(1);
|
|
121
|
-
name = name + suffix;
|
|
122
|
-
}
|
|
123
|
-
return name;
|
|
124
|
-
}
|
|
125
|
-
function toPascalCase(str) {
|
|
126
|
-
const stringValue = String(str);
|
|
127
|
-
const isAlreadyValidCase = /^[a-zA-Z][a-zA-Z0-9]*$/.test(stringValue);
|
|
128
|
-
if (isAlreadyValidCase) {
|
|
129
|
-
return stringValue.charAt(0).toUpperCase() + stringValue.slice(1);
|
|
130
|
-
}
|
|
131
|
-
const sanitized = sanitizeIdentifier(stringValue);
|
|
132
|
-
const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
|
|
133
|
-
let result;
|
|
134
|
-
if (words.length === 0) {
|
|
135
|
-
result = "Value";
|
|
136
|
-
} else {
|
|
137
|
-
result = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
138
|
-
}
|
|
139
|
-
if (/^\d/.test(result)) {
|
|
140
|
-
result = `N${result}`;
|
|
141
|
-
}
|
|
142
|
-
if (!result || /^_+$/.test(result)) {
|
|
143
|
-
return "Value";
|
|
144
|
-
}
|
|
145
|
-
return result;
|
|
146
|
-
}
|
|
147
|
-
function resolveRef(ref) {
|
|
148
|
-
const parts = ref.split("/");
|
|
149
|
-
return parts[parts.length - 1];
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// src/generators/enum-generator.ts
|
|
153
|
-
function generateEnum(name, values, options) {
|
|
154
|
-
const schemaName = `${toCamelCase(name, options)}Schema`;
|
|
155
|
-
const typeName = toPascalCase(name);
|
|
156
|
-
const allBooleans = values.every((v) => typeof v === "boolean");
|
|
157
|
-
if (allBooleans) {
|
|
158
|
-
const schemaCode2 = `export const ${schemaName} = z.boolean();`;
|
|
159
|
-
const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
|
|
160
|
-
return { schemaCode: schemaCode2, typeCode: typeCode2 };
|
|
161
|
-
}
|
|
162
|
-
const allStrings = values.every((v) => typeof v === "string");
|
|
163
|
-
if (allStrings) {
|
|
164
|
-
const enumValues = values.map((v) => `"${v}"`).join(", ");
|
|
165
|
-
const schemaCode2 = `export const ${schemaName} = z.enum([${enumValues}]);`;
|
|
166
|
-
const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
|
|
167
|
-
return { schemaCode: schemaCode2, typeCode: typeCode2 };
|
|
168
|
-
}
|
|
169
|
-
const literalValues = values.map((v) => {
|
|
170
|
-
if (typeof v === "string") {
|
|
171
|
-
return `z.literal("${v}")`;
|
|
172
|
-
}
|
|
173
|
-
return `z.literal(${v})`;
|
|
174
|
-
}).join(", ");
|
|
175
|
-
const schemaCode = `export const ${schemaName} = z.union([${literalValues}]);`;
|
|
176
|
-
const typeCode = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
|
|
177
|
-
return { schemaCode, typeCode };
|
|
178
|
-
}
|
|
38
|
+
// src/generators/property-generator.ts
|
|
39
|
+
var import_openapi_core4 = require("@cerios/openapi-core");
|
|
179
40
|
|
|
180
41
|
// src/utils/string-utils.ts
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
184
|
-
function escapePattern(str) {
|
|
185
|
-
return str.replace(/\//g, "\\/");
|
|
186
|
-
}
|
|
187
|
-
function escapeJSDoc(str) {
|
|
188
|
-
return str.replace(/\*\//g, "*\\/");
|
|
189
|
-
}
|
|
190
|
-
function wrapNullable(validation, isNullable2) {
|
|
191
|
-
return isNullable2 ? `${validation}.nullable()` : validation;
|
|
192
|
-
}
|
|
193
|
-
function isNullable(schema, defaultNullable = false) {
|
|
194
|
-
if (schema.nullable === true) {
|
|
195
|
-
return true;
|
|
196
|
-
}
|
|
197
|
-
if (schema.nullable === false) {
|
|
198
|
-
return false;
|
|
199
|
-
}
|
|
200
|
-
if (Array.isArray(schema.type)) {
|
|
201
|
-
return schema.type.includes("null");
|
|
202
|
-
}
|
|
203
|
-
return defaultNullable;
|
|
204
|
-
}
|
|
205
|
-
function getPrimaryType(schema) {
|
|
206
|
-
if (Array.isArray(schema.type)) {
|
|
207
|
-
const nonNullType = schema.type.find((t) => t !== "null");
|
|
208
|
-
return nonNullType;
|
|
209
|
-
}
|
|
210
|
-
return schema.type;
|
|
211
|
-
}
|
|
212
|
-
function hasMultipleTypes(schema) {
|
|
213
|
-
if (Array.isArray(schema.type)) {
|
|
214
|
-
const nonNullTypes = schema.type.filter((t) => t !== "null");
|
|
215
|
-
return nonNullTypes.length > 1;
|
|
216
|
-
}
|
|
217
|
-
return false;
|
|
42
|
+
var import_openapi_core = require("@cerios/openapi-core");
|
|
43
|
+
function wrapNullable(validation, nullable) {
|
|
44
|
+
return nullable ? `${validation}.nullable()` : validation;
|
|
218
45
|
}
|
|
219
46
|
function addDescription(validation, description, useDescribe) {
|
|
220
47
|
if (!description || !useDescribe) return validation;
|
|
221
|
-
const escapedDesc = escapeDescription(description);
|
|
48
|
+
const escapedDesc = (0, import_openapi_core.escapeDescription)(description);
|
|
222
49
|
return `${validation}.describe("${escapedDesc}")`;
|
|
223
50
|
}
|
|
224
51
|
|
|
225
|
-
// src/generators/jsdoc-generator.ts
|
|
226
|
-
function generateJSDoc(schema, name, options = { includeDescriptions: true }) {
|
|
227
|
-
if (!schema || typeof schema !== "object") {
|
|
228
|
-
return "";
|
|
229
|
-
}
|
|
230
|
-
if (!options.includeDescriptions) {
|
|
231
|
-
if (schema.deprecated) {
|
|
232
|
-
return "/** @deprecated */\n";
|
|
233
|
-
}
|
|
234
|
-
return "";
|
|
235
|
-
}
|
|
236
|
-
if (!schema.description && !schema.title && !schema.deprecated && !schema.examples && schema.example === void 0) {
|
|
237
|
-
return "";
|
|
238
|
-
}
|
|
239
|
-
const parts = [];
|
|
240
|
-
if (schema.title && typeof schema.title === "string" && (!name || schema.title !== name)) {
|
|
241
|
-
const sanitizedTitle = escapeJSDoc(schema.title).replace(/@/g, "\\@");
|
|
242
|
-
parts.push(sanitizedTitle);
|
|
243
|
-
}
|
|
244
|
-
if (schema.description && typeof schema.description === "string") {
|
|
245
|
-
const sanitizedDesc = escapeJSDoc(schema.description).replace(/@/g, "\\@").replace(/\*\//g, "*\\/");
|
|
246
|
-
parts.push(sanitizedDesc);
|
|
247
|
-
}
|
|
248
|
-
if (schema.examples && Array.isArray(schema.examples) && schema.examples.length > 0) {
|
|
249
|
-
try {
|
|
250
|
-
const examplesStr = schema.examples.map((ex) => JSON.stringify(ex)).join(", ");
|
|
251
|
-
parts.push(`@example ${examplesStr}`);
|
|
252
|
-
} catch (error) {
|
|
253
|
-
console.warn("Warning: Could not serialize schema examples", error);
|
|
254
|
-
}
|
|
255
|
-
} else if (schema.example !== void 0) {
|
|
256
|
-
try {
|
|
257
|
-
parts.push(`@example ${JSON.stringify(schema.example)}`);
|
|
258
|
-
} catch (error) {
|
|
259
|
-
console.warn("Warning: Could not serialize schema example", error);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
if (schema.deprecated) {
|
|
263
|
-
parts.push("@deprecated");
|
|
264
|
-
}
|
|
265
|
-
if (parts.length === 0) {
|
|
266
|
-
return "";
|
|
267
|
-
}
|
|
268
|
-
const fullComment = parts.join(" ");
|
|
269
|
-
return `/** ${fullComment} */
|
|
270
|
-
`;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
// src/utils/lru-cache.ts
|
|
274
|
-
var LRUCache = class {
|
|
275
|
-
constructor(maxSize) {
|
|
276
|
-
this.cache = /* @__PURE__ */ new Map();
|
|
277
|
-
this.maxSize = maxSize;
|
|
278
|
-
}
|
|
279
|
-
get capacity() {
|
|
280
|
-
return this.maxSize;
|
|
281
|
-
}
|
|
282
|
-
get(key) {
|
|
283
|
-
if (!this.cache.has(key)) return void 0;
|
|
284
|
-
const value = this.cache.get(key);
|
|
285
|
-
if (value === void 0) return void 0;
|
|
286
|
-
this.cache.delete(key);
|
|
287
|
-
this.cache.set(key, value);
|
|
288
|
-
return value;
|
|
289
|
-
}
|
|
290
|
-
set(key, value) {
|
|
291
|
-
if (this.cache.has(key)) {
|
|
292
|
-
this.cache.delete(key);
|
|
293
|
-
} else if (this.cache.size >= this.maxSize) {
|
|
294
|
-
const firstKey = this.cache.keys().next().value;
|
|
295
|
-
if (firstKey !== void 0) {
|
|
296
|
-
this.cache.delete(firstKey);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
this.cache.set(key, value);
|
|
300
|
-
}
|
|
301
|
-
has(key) {
|
|
302
|
-
return this.cache.has(key);
|
|
303
|
-
}
|
|
304
|
-
clear() {
|
|
305
|
-
this.cache.clear();
|
|
306
|
-
}
|
|
307
|
-
size() {
|
|
308
|
-
return this.cache.size;
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
// src/utils/pattern-utils.ts
|
|
313
|
-
var import_minimatch = require("minimatch");
|
|
314
|
-
function isValidGlobPattern(pattern) {
|
|
315
|
-
try {
|
|
316
|
-
new import_minimatch.minimatch.Minimatch(pattern);
|
|
317
|
-
return true;
|
|
318
|
-
} catch {
|
|
319
|
-
return false;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
function isGlobPattern(pattern) {
|
|
323
|
-
return /[*?[\]{}!]/.test(pattern);
|
|
324
|
-
}
|
|
325
|
-
function stripPrefix(input, pattern, ensureLeadingChar) {
|
|
326
|
-
if (!pattern) {
|
|
327
|
-
return input;
|
|
328
|
-
}
|
|
329
|
-
if (isGlobPattern(pattern) && !isValidGlobPattern(pattern)) {
|
|
330
|
-
console.warn(`\u26A0\uFE0F Invalid glob pattern "${pattern}": Pattern is malformed`);
|
|
331
|
-
return input;
|
|
332
|
-
}
|
|
333
|
-
if (isGlobPattern(pattern)) {
|
|
334
|
-
let longestMatch = -1;
|
|
335
|
-
for (let i = 1; i <= input.length; i++) {
|
|
336
|
-
const testPrefix = input.substring(0, i);
|
|
337
|
-
if ((0, import_minimatch.minimatch)(testPrefix, pattern)) {
|
|
338
|
-
longestMatch = i;
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
if (longestMatch > 0) {
|
|
342
|
-
const stripped = input.substring(longestMatch);
|
|
343
|
-
if (ensureLeadingChar) {
|
|
344
|
-
if (stripped === "") {
|
|
345
|
-
return ensureLeadingChar;
|
|
346
|
-
}
|
|
347
|
-
if (!stripped.startsWith(ensureLeadingChar)) {
|
|
348
|
-
return `${ensureLeadingChar}${stripped}`;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
return stripped === "" && !ensureLeadingChar ? input : stripped;
|
|
352
|
-
}
|
|
353
|
-
return input;
|
|
354
|
-
}
|
|
355
|
-
if (input.startsWith(pattern)) {
|
|
356
|
-
const stripped = input.substring(pattern.length);
|
|
357
|
-
if (ensureLeadingChar) {
|
|
358
|
-
if (stripped === "") {
|
|
359
|
-
return ensureLeadingChar;
|
|
360
|
-
}
|
|
361
|
-
if (!stripped.startsWith(ensureLeadingChar)) {
|
|
362
|
-
return `${ensureLeadingChar}${stripped}`;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
return stripped;
|
|
366
|
-
}
|
|
367
|
-
return input;
|
|
368
|
-
}
|
|
369
|
-
function stripPathPrefix(path, pattern) {
|
|
370
|
-
if (!pattern) {
|
|
371
|
-
return path;
|
|
372
|
-
}
|
|
373
|
-
if (!isGlobPattern(pattern)) {
|
|
374
|
-
let normalizedPattern = pattern.trim();
|
|
375
|
-
if (!normalizedPattern.startsWith("/")) {
|
|
376
|
-
normalizedPattern = `/${normalizedPattern}`;
|
|
377
|
-
}
|
|
378
|
-
if (normalizedPattern.endsWith("/") && normalizedPattern !== "/") {
|
|
379
|
-
normalizedPattern = normalizedPattern.slice(0, -1);
|
|
380
|
-
}
|
|
381
|
-
return stripPrefix(path, normalizedPattern, "/");
|
|
382
|
-
}
|
|
383
|
-
return stripPrefix(path, pattern, "/");
|
|
384
|
-
}
|
|
385
|
-
|
|
386
52
|
// src/validators/array-validator.ts
|
|
387
53
|
function generateArrayValidation(schema, context) {
|
|
388
54
|
var _a;
|
|
@@ -550,15 +216,14 @@ function detectConflictingProperties(schemas, context) {
|
|
|
550
216
|
function generateAllOf(schemas, isNullable2, context, currentSchema) {
|
|
551
217
|
if (schemas.length === 1) {
|
|
552
218
|
const singleSchema = context.generatePropertySchema(schemas[0], currentSchema, false, true);
|
|
553
|
-
return wrapNullable(singleSchema, isNullable2);
|
|
219
|
+
return { schema: wrapNullable(singleSchema, isNullable2), conflicts: [] };
|
|
554
220
|
}
|
|
555
221
|
const conflicts = detectConflictingProperties(schemas, context);
|
|
556
|
-
|
|
557
|
-
if (
|
|
558
|
-
for (const conflict of
|
|
222
|
+
const uniqueConflicts = [...new Set(conflicts)];
|
|
223
|
+
if (uniqueConflicts.length > 0) {
|
|
224
|
+
for (const conflict of uniqueConflicts) {
|
|
559
225
|
console.warn(`[openapi-to-zod] Warning: allOf composition conflict - ${conflict}`);
|
|
560
226
|
}
|
|
561
|
-
conflictDescription = `allOf property conflicts detected: ${conflicts.join("; ")}`;
|
|
562
227
|
}
|
|
563
228
|
const allObjects = schemas.every((s) => s.type === "object" || s.properties || s.$ref || s.allOf);
|
|
564
229
|
let result;
|
|
@@ -586,10 +251,7 @@ function generateAllOf(schemas, isNullable2, context, currentSchema) {
|
|
|
586
251
|
}
|
|
587
252
|
result = merged;
|
|
588
253
|
}
|
|
589
|
-
|
|
590
|
-
result = `${result}.describe("${conflictDescription}")`;
|
|
591
|
-
}
|
|
592
|
-
return wrapNullable(result, isNullable2);
|
|
254
|
+
return { schema: wrapNullable(result, isNullable2), conflicts: uniqueConflicts };
|
|
593
255
|
}
|
|
594
256
|
|
|
595
257
|
// src/validators/number-validator.ts
|
|
@@ -613,6 +275,53 @@ function generateNumberValidation(schema, isInt, useDescribe) {
|
|
|
613
275
|
return addDescription(validation, schema.description, useDescribe);
|
|
614
276
|
}
|
|
615
277
|
|
|
278
|
+
// src/generators/jsdoc-generator.ts
|
|
279
|
+
var import_openapi_core2 = require("@cerios/openapi-core");
|
|
280
|
+
function generateJSDoc(schema, name, options = { includeDescriptions: true }) {
|
|
281
|
+
if (!schema || typeof schema !== "object") {
|
|
282
|
+
return "";
|
|
283
|
+
}
|
|
284
|
+
if (!options.includeDescriptions) {
|
|
285
|
+
if (schema.deprecated) {
|
|
286
|
+
return "/** @deprecated */\n";
|
|
287
|
+
}
|
|
288
|
+
return "";
|
|
289
|
+
}
|
|
290
|
+
if (!schema.description && !schema.title && !schema.deprecated && !schema.examples && schema.example === void 0) {
|
|
291
|
+
return "";
|
|
292
|
+
}
|
|
293
|
+
const parts = [];
|
|
294
|
+
if (schema.title && typeof schema.title === "string" && (!name || schema.title !== name)) {
|
|
295
|
+
parts.push((0, import_openapi_core2.escapeJSDoc)(schema.title));
|
|
296
|
+
}
|
|
297
|
+
if (schema.description && typeof schema.description === "string") {
|
|
298
|
+
parts.push((0, import_openapi_core2.escapeJSDoc)(schema.description));
|
|
299
|
+
}
|
|
300
|
+
if (schema.examples && Array.isArray(schema.examples) && schema.examples.length > 0) {
|
|
301
|
+
try {
|
|
302
|
+
const examplesStr = schema.examples.map((ex) => JSON.stringify(ex)).join(", ");
|
|
303
|
+
parts.push(`@example ${examplesStr}`);
|
|
304
|
+
} catch (error) {
|
|
305
|
+
console.warn("Warning: Could not serialize schema examples", error);
|
|
306
|
+
}
|
|
307
|
+
} else if (schema.example !== void 0) {
|
|
308
|
+
try {
|
|
309
|
+
parts.push(`@example ${JSON.stringify(schema.example)}`);
|
|
310
|
+
} catch (error) {
|
|
311
|
+
console.warn("Warning: Could not serialize schema example", error);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (schema.deprecated) {
|
|
315
|
+
parts.push("@deprecated");
|
|
316
|
+
}
|
|
317
|
+
if (parts.length === 0) {
|
|
318
|
+
return "";
|
|
319
|
+
}
|
|
320
|
+
const fullComment = parts.join(" ");
|
|
321
|
+
return `/** ${fullComment} */
|
|
322
|
+
`;
|
|
323
|
+
}
|
|
324
|
+
|
|
616
325
|
// src/validators/conditional-validator.ts
|
|
617
326
|
function generatePropertyAccess(propName) {
|
|
618
327
|
const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
@@ -671,7 +380,8 @@ function generateConditionalCheck(schema) {
|
|
|
671
380
|
for (const [prop, propSchema] of Object.entries(schema.properties)) {
|
|
672
381
|
const propAccess = generatePropertyAccess(prop);
|
|
673
382
|
if (propSchema.type) {
|
|
674
|
-
|
|
383
|
+
const schemaType = Array.isArray(propSchema.type) ? propSchema.type[0] : propSchema.type;
|
|
384
|
+
conditions.push(`typeof ${propAccess} === "${schemaType}"`);
|
|
675
385
|
}
|
|
676
386
|
if (propSchema.const !== void 0) {
|
|
677
387
|
const value = typeof propSchema.const === "string" ? `"${propSchema.const}"` : propSchema.const;
|
|
@@ -730,7 +440,7 @@ function generateIfThenElse(schema) {
|
|
|
730
440
|
if (!thenValid) {
|
|
731
441
|
${thenRequiredProps.length > 0 ? `
|
|
732
442
|
const missingThenProps = ${JSON.stringify(thenRequiredProps)}.filter(p => obj[p] === undefined);
|
|
733
|
-
const message = missingThenProps.length > 0
|
|
443
|
+
const message = missingThenProps.length > 0
|
|
734
444
|
? \`When condition is met, required properties are missing: \${missingThenProps.join(', ')}\`
|
|
735
445
|
: "When condition is met, validation constraints failed";
|
|
736
446
|
` : `
|
|
@@ -748,7 +458,7 @@ function generateIfThenElse(schema) {
|
|
|
748
458
|
if (!elseValid) {
|
|
749
459
|
${elseRequiredProps2.length > 0 ? `
|
|
750
460
|
const missingElseProps = ${JSON.stringify(elseRequiredProps2)}.filter(p => obj[p] === undefined);
|
|
751
|
-
const message = missingElseProps.length > 0
|
|
461
|
+
const message = missingElseProps.length > 0
|
|
752
462
|
? \`When condition is not met, required properties are missing: \${missingElseProps.join(', ')}\`
|
|
753
463
|
: "When condition is not met, validation constraints failed";
|
|
754
464
|
` : `
|
|
@@ -773,7 +483,7 @@ function generateIfThenElse(schema) {
|
|
|
773
483
|
if (!thenValid) {
|
|
774
484
|
${thenRequiredProps.length > 0 ? `
|
|
775
485
|
const missingProps = ${JSON.stringify(thenRequiredProps)}.filter(p => obj[p] === undefined);
|
|
776
|
-
const message = missingProps.length > 0
|
|
486
|
+
const message = missingProps.length > 0
|
|
777
487
|
? \`When condition is met, required properties are missing: \${missingProps.join(', ')}\`
|
|
778
488
|
: "When condition is met, validation constraints failed";
|
|
779
489
|
` : `
|
|
@@ -798,7 +508,7 @@ function generateIfThenElse(schema) {
|
|
|
798
508
|
if (!elseValid) {
|
|
799
509
|
${elseRequiredProps.length > 0 ? `
|
|
800
510
|
const missingProps = ${JSON.stringify(elseRequiredProps)}.filter(p => obj[p] === undefined);
|
|
801
|
-
const message = missingProps.length > 0
|
|
511
|
+
const message = missingProps.length > 0
|
|
802
512
|
? \`When condition is not met, required properties are missing: \${missingProps.join(', ')}\`
|
|
803
513
|
: "When condition is not met, validation constraints failed";
|
|
804
514
|
` : `
|
|
@@ -886,8 +596,9 @@ ${propertyDef}`);
|
|
|
886
596
|
case "loose":
|
|
887
597
|
objectMethod = "z.looseObject";
|
|
888
598
|
break;
|
|
889
|
-
|
|
599
|
+
case "normal":
|
|
890
600
|
objectMethod = "z.object";
|
|
601
|
+
break;
|
|
891
602
|
}
|
|
892
603
|
}
|
|
893
604
|
let objectDef = `${objectMethod}({
|
|
@@ -897,7 +608,7 @@ ${properties.join(",\n")}
|
|
|
897
608
|
if (typeof schema.additionalProperties === "object") {
|
|
898
609
|
const additionalSchema = context.generatePropertySchema(schema.additionalProperties, currentSchema);
|
|
899
610
|
objectDef += `.catchall(${additionalSchema})`;
|
|
900
|
-
} else if (schema.additionalProperties
|
|
611
|
+
} else if (schema.additionalProperties) {
|
|
901
612
|
objectDef += ".catchall(z.unknown())";
|
|
902
613
|
}
|
|
903
614
|
} else if (schema.patternProperties) {
|
|
@@ -1022,12 +733,7 @@ ${properties.join(",\n")}
|
|
|
1022
733
|
}
|
|
1023
734
|
|
|
1024
735
|
// src/validators/string-validator.ts
|
|
1025
|
-
var
|
|
1026
|
-
function configurePatternCache(size) {
|
|
1027
|
-
if (size > 0 && size !== PATTERN_CACHE.capacity) {
|
|
1028
|
-
PATTERN_CACHE = new LRUCache(size);
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
736
|
+
var import_openapi_core3 = require("@cerios/openapi-core");
|
|
1031
737
|
var DEFAULT_FORMAT_MAP = {
|
|
1032
738
|
uuid: "z.uuid()",
|
|
1033
739
|
email: "z.email()",
|
|
@@ -1056,19 +762,13 @@ var DEFAULT_FORMAT_MAP = {
|
|
|
1056
762
|
"json-pointer": 'z.string().refine((val) => val === "" || /^(\\/([^~/]|~0|~1)+)+$/.test(val), { message: "Must be a valid JSON Pointer (RFC 6901)" })',
|
|
1057
763
|
"relative-json-pointer": 'z.string().refine((val) => /^(0|[1-9]\\d*)(#|(\\/([^~/]|~0|~1)+)*)$/.test(val), { message: "Must be a valid relative JSON Pointer" })'
|
|
1058
764
|
};
|
|
1059
|
-
|
|
1060
|
-
...DEFAULT_FORMAT_MAP,
|
|
1061
|
-
"date-time": "z.iso.datetime()"
|
|
1062
|
-
};
|
|
1063
|
-
function configureDateTimeFormat(pattern) {
|
|
765
|
+
function buildDateTimeValidation(pattern) {
|
|
1064
766
|
if (!pattern) {
|
|
1065
|
-
|
|
1066
|
-
return;
|
|
767
|
+
return "z.iso.datetime()";
|
|
1067
768
|
}
|
|
1068
769
|
const patternStr = pattern instanceof RegExp ? pattern.source : pattern;
|
|
1069
770
|
if (patternStr === "") {
|
|
1070
|
-
|
|
1071
|
-
return;
|
|
771
|
+
return "z.iso.datetime()";
|
|
1072
772
|
}
|
|
1073
773
|
try {
|
|
1074
774
|
new RegExp(patternStr);
|
|
@@ -1077,11 +777,17 @@ function configureDateTimeFormat(pattern) {
|
|
|
1077
777
|
`Invalid regular expression pattern for customDateTimeFormatRegex: ${patternStr}. ${error instanceof Error ? error.message : "Pattern is malformed"}`
|
|
1078
778
|
);
|
|
1079
779
|
}
|
|
1080
|
-
const escapedPattern = escapePattern(patternStr);
|
|
1081
|
-
|
|
780
|
+
const escapedPattern = (0, import_openapi_core3.escapePattern)(patternStr);
|
|
781
|
+
return `z.string().regex(/${escapedPattern}/)`;
|
|
1082
782
|
}
|
|
1083
|
-
function generateStringValidation(schema, useDescribe) {
|
|
1084
|
-
let validation
|
|
783
|
+
function generateStringValidation(schema, useDescribe, context) {
|
|
784
|
+
let validation;
|
|
785
|
+
const format = schema.format || "";
|
|
786
|
+
if (format === "date-time") {
|
|
787
|
+
validation = context.dateTimeValidation;
|
|
788
|
+
} else {
|
|
789
|
+
validation = DEFAULT_FORMAT_MAP[format] || "z.string()";
|
|
790
|
+
}
|
|
1085
791
|
if (schema.minLength !== void 0) {
|
|
1086
792
|
validation += `.min(${schema.minLength})`;
|
|
1087
793
|
}
|
|
@@ -1089,10 +795,10 @@ function generateStringValidation(schema, useDescribe) {
|
|
|
1089
795
|
validation += `.max(${schema.maxLength})`;
|
|
1090
796
|
}
|
|
1091
797
|
if (schema.pattern) {
|
|
1092
|
-
let escapedPattern =
|
|
798
|
+
let escapedPattern = context.patternCache.get(schema.pattern);
|
|
1093
799
|
if (escapedPattern === void 0) {
|
|
1094
|
-
escapedPattern = escapePattern(schema.pattern);
|
|
1095
|
-
|
|
800
|
+
escapedPattern = (0, import_openapi_core3.escapePattern)(schema.pattern);
|
|
801
|
+
context.patternCache.set(schema.pattern, escapedPattern);
|
|
1096
802
|
}
|
|
1097
803
|
validation += `.regex(/${escapedPattern}/)`;
|
|
1098
804
|
}
|
|
@@ -1122,10 +828,10 @@ function generateStringValidation(schema, useDescribe) {
|
|
|
1122
828
|
validation += `.max(${schema.maxLength})`;
|
|
1123
829
|
}
|
|
1124
830
|
if (schema.pattern) {
|
|
1125
|
-
let escapedPattern =
|
|
831
|
+
let escapedPattern = context.patternCache.get(schema.pattern);
|
|
1126
832
|
if (escapedPattern === void 0) {
|
|
1127
|
-
escapedPattern = escapePattern(schema.pattern);
|
|
1128
|
-
|
|
833
|
+
escapedPattern = (0, import_openapi_core3.escapePattern)(schema.pattern);
|
|
834
|
+
context.patternCache.set(schema.pattern, escapedPattern);
|
|
1129
835
|
}
|
|
1130
836
|
validation += `.regex(/${escapedPattern}/)`;
|
|
1131
837
|
}
|
|
@@ -1152,9 +858,33 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
1152
858
|
// Performance optimization: Memoize filtered property results
|
|
1153
859
|
this.filteredPropsCache = /* @__PURE__ */ new Map();
|
|
1154
860
|
// Performance optimization: LRU cache for generated schemas
|
|
1155
|
-
this.schemaCache = new LRUCache(500);
|
|
861
|
+
this.schemaCache = new import_openapi_core4.LRUCache(500);
|
|
862
|
+
// Track allOf conflicts detected during schema generation
|
|
863
|
+
this.allOfConflicts = [];
|
|
864
|
+
// Schemas that are part of circular dependency chains (need z.lazy for forward refs)
|
|
865
|
+
this.circularDependencies = /* @__PURE__ */ new Set();
|
|
1156
866
|
this.context = context;
|
|
1157
867
|
}
|
|
868
|
+
/**
|
|
869
|
+
* Set the schemas that are involved in circular dependency chains.
|
|
870
|
+
* These schemas will use z.lazy() for forward references.
|
|
871
|
+
*/
|
|
872
|
+
setCircularDependencies(deps) {
|
|
873
|
+
this.circularDependencies = deps;
|
|
874
|
+
}
|
|
875
|
+
/**
|
|
876
|
+
* Get allOf conflicts detected during the last schema generation
|
|
877
|
+
* @returns Array of conflict description strings
|
|
878
|
+
*/
|
|
879
|
+
getAllOfConflicts() {
|
|
880
|
+
return [...this.allOfConflicts];
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Clear tracked allOf conflicts (call before generating a new schema)
|
|
884
|
+
*/
|
|
885
|
+
clearAllOfConflicts() {
|
|
886
|
+
this.allOfConflicts = [];
|
|
887
|
+
}
|
|
1158
888
|
/**
|
|
1159
889
|
* Check if a property should be included based on schemaType and readOnly/writeOnly flags
|
|
1160
890
|
*/
|
|
@@ -1187,7 +917,9 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
1187
917
|
filterNestedProperties(schema) {
|
|
1188
918
|
var _a, _b;
|
|
1189
919
|
const propKeys = schema.properties ? Object.keys(schema.properties).sort().join(",") : "";
|
|
1190
|
-
const
|
|
920
|
+
const requiredKeys = Array.isArray(schema.required) ? schema.required.join(",") : String((_a = schema.required) != null ? _a : "");
|
|
921
|
+
const schemaType = Array.isArray(schema.type) ? schema.type.join("|") : schema.type || "unknown";
|
|
922
|
+
const cacheKey = `${this.context.schemaType}:${schemaType}:${propKeys}:${requiredKeys}`;
|
|
1191
923
|
const cached = this.filteredPropsCache.get(cacheKey);
|
|
1192
924
|
if (cached) {
|
|
1193
925
|
return cached;
|
|
@@ -1283,7 +1015,7 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
1283
1015
|
const schema = (_b = (_a = this.context.spec.components) == null ? void 0 : _a.schemas) == null ? void 0 : _b[schemaName];
|
|
1284
1016
|
if (!schema) return schemaName;
|
|
1285
1017
|
if (schema.allOf && schema.allOf.length === 1 && schema.allOf[0].$ref && !schema.properties && !schema.oneOf && !schema.anyOf) {
|
|
1286
|
-
const targetName =
|
|
1018
|
+
const targetName = (0, import_openapi_core4.resolveRefName)(schema.allOf[0].$ref);
|
|
1287
1019
|
return this.resolveSchemaAlias(targetName);
|
|
1288
1020
|
}
|
|
1289
1021
|
return schemaName;
|
|
@@ -1296,7 +1028,7 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
1296
1028
|
const toSchemaSpec = (_b = (_a = this.context.spec.components) == null ? void 0 : _a.schemas) == null ? void 0 : _b[toSchema];
|
|
1297
1029
|
if (!toSchemaSpec) return false;
|
|
1298
1030
|
if (toSchemaSpec.allOf && toSchemaSpec.allOf.length === 1 && toSchemaSpec.allOf[0].$ref) {
|
|
1299
|
-
const aliasTarget =
|
|
1031
|
+
const aliasTarget = (0, import_openapi_core4.resolveRefName)(toSchemaSpec.allOf[0].$ref);
|
|
1300
1032
|
return aliasTarget === fromSchema;
|
|
1301
1033
|
}
|
|
1302
1034
|
return false;
|
|
@@ -1391,13 +1123,13 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
1391
1123
|
const isConst = schema.const !== void 0;
|
|
1392
1124
|
const shouldApplyDefaultNullable = !isTopLevel && !isEnum && !isConst && !suppressDefaultNullable;
|
|
1393
1125
|
const effectiveDefaultNullable = shouldApplyDefaultNullable ? this.context.defaultNullable : false;
|
|
1394
|
-
const nullable = isNullable(schema, effectiveDefaultNullable);
|
|
1395
|
-
if (hasMultipleTypes(schema)) {
|
|
1126
|
+
const nullable = (0, import_openapi_core4.isNullable)(schema, effectiveDefaultNullable);
|
|
1127
|
+
if ((0, import_openapi_core4.hasMultipleTypes)(schema)) {
|
|
1396
1128
|
const union = this.generateMultiTypeUnion(schema, currentSchema);
|
|
1397
1129
|
return wrapNullable(union, nullable);
|
|
1398
1130
|
}
|
|
1399
1131
|
if (schema.$ref) {
|
|
1400
|
-
const refName =
|
|
1132
|
+
const refName = (0, import_openapi_core4.resolveRefName)(schema.$ref);
|
|
1401
1133
|
const resolvedRefName = this.resolveSchemaAlias(refName);
|
|
1402
1134
|
if (currentSchema && refName !== currentSchema && !isTopLevel) {
|
|
1403
1135
|
if (!this.context.schemaDependencies.has(currentSchema)) {
|
|
@@ -1405,10 +1137,15 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
1405
1137
|
}
|
|
1406
1138
|
(_a = this.context.schemaDependencies.get(currentSchema)) == null ? void 0 : _a.add(refName);
|
|
1407
1139
|
}
|
|
1408
|
-
const strippedRefName = stripPrefix(resolvedRefName, this.context.stripSchemaPrefix);
|
|
1409
|
-
const schemaName = `${toCamelCase(strippedRefName, this.context.namingOptions)}Schema`;
|
|
1410
|
-
|
|
1411
|
-
|
|
1140
|
+
const strippedRefName = (0, import_openapi_core4.stripPrefix)(resolvedRefName, this.context.stripSchemaPrefix);
|
|
1141
|
+
const schemaName = `${(0, import_openapi_core4.toCamelCase)(strippedRefName, this.context.namingOptions)}Schema`;
|
|
1142
|
+
const typeName = (0, import_openapi_core4.toPascalCase)(strippedRefName);
|
|
1143
|
+
const isDirectSelfRef = currentSchema && refName === currentSchema;
|
|
1144
|
+
const isCircularAlias = currentSchema && this.isCircularThroughAlias(currentSchema, refName);
|
|
1145
|
+
const isMutuallyCircular = currentSchema && this.circularDependencies.has(currentSchema) && this.circularDependencies.has(refName);
|
|
1146
|
+
if (isDirectSelfRef || isCircularAlias || isMutuallyCircular) {
|
|
1147
|
+
const lazyTypeAnnotation = this.context.separateTypesFile ? `z.ZodType<${typeName}>` : "z.ZodTypeAny";
|
|
1148
|
+
const lazySchema = `z.lazy((): ${lazyTypeAnnotation} => ${schemaName})`;
|
|
1412
1149
|
return wrapNullable(lazySchema, nullable);
|
|
1413
1150
|
}
|
|
1414
1151
|
return wrapNullable(schemaName, nullable);
|
|
@@ -1440,8 +1177,8 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
1440
1177
|
return wrapNullable(zodUnion, nullable);
|
|
1441
1178
|
}
|
|
1442
1179
|
if (schema.allOf) {
|
|
1443
|
-
const compositionNullable = isNullable(schema, false);
|
|
1444
|
-
|
|
1180
|
+
const compositionNullable = (0, import_openapi_core4.isNullable)(schema, false);
|
|
1181
|
+
const allOfResult = generateAllOf(
|
|
1445
1182
|
schema.allOf,
|
|
1446
1183
|
compositionNullable,
|
|
1447
1184
|
{
|
|
@@ -1451,13 +1188,17 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
1451
1188
|
},
|
|
1452
1189
|
currentSchema
|
|
1453
1190
|
);
|
|
1191
|
+
if (allOfResult.conflicts.length > 0) {
|
|
1192
|
+
this.allOfConflicts.push(...allOfResult.conflicts);
|
|
1193
|
+
}
|
|
1194
|
+
let composition = allOfResult.schema;
|
|
1454
1195
|
if (schema.unevaluatedProperties !== void 0) {
|
|
1455
1196
|
composition = this.applyUnevaluatedProperties(composition, schema);
|
|
1456
1197
|
}
|
|
1457
1198
|
return composition;
|
|
1458
1199
|
}
|
|
1459
1200
|
if (schema.oneOf) {
|
|
1460
|
-
const compositionNullable = isNullable(schema, false);
|
|
1201
|
+
const compositionNullable = (0, import_openapi_core4.isNullable)(schema, false);
|
|
1461
1202
|
const needsPassthrough = schema.unevaluatedProperties !== void 0;
|
|
1462
1203
|
let composition = generateUnion(
|
|
1463
1204
|
schema.oneOf,
|
|
@@ -1480,7 +1221,7 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
1480
1221
|
return composition;
|
|
1481
1222
|
}
|
|
1482
1223
|
if (schema.anyOf) {
|
|
1483
|
-
const compositionNullable = isNullable(schema, false);
|
|
1224
|
+
const compositionNullable = (0, import_openapi_core4.isNullable)(schema, false);
|
|
1484
1225
|
const needsPassthrough = schema.unevaluatedProperties !== void 0;
|
|
1485
1226
|
let composition = generateUnion(
|
|
1486
1227
|
schema.anyOf,
|
|
@@ -1515,10 +1256,13 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
1515
1256
|
return wrapNullable(refined, nullable);
|
|
1516
1257
|
}
|
|
1517
1258
|
let validation = "";
|
|
1518
|
-
const primaryType = getPrimaryType(schema);
|
|
1259
|
+
const primaryType = (0, import_openapi_core4.getPrimaryType)(schema);
|
|
1519
1260
|
switch (primaryType) {
|
|
1520
1261
|
case "string":
|
|
1521
|
-
validation = generateStringValidation(schema, this.context.useDescribe
|
|
1262
|
+
validation = generateStringValidation(schema, this.context.useDescribe, {
|
|
1263
|
+
dateTimeValidation: this.context.dateTimeValidation,
|
|
1264
|
+
patternCache: this.context.patternCache
|
|
1265
|
+
});
|
|
1522
1266
|
break;
|
|
1523
1267
|
case "number":
|
|
1524
1268
|
validation = generateNumberValidation(schema, false, this.context.useDescribe);
|
|
@@ -1559,13 +1303,14 @@ var _PropertyGenerator = class _PropertyGenerator {
|
|
|
1559
1303
|
case "loose":
|
|
1560
1304
|
validation = "z.looseObject({})";
|
|
1561
1305
|
break;
|
|
1562
|
-
|
|
1306
|
+
case "record":
|
|
1563
1307
|
validation = "z.record(z.string(), z.unknown())";
|
|
1564
1308
|
break;
|
|
1565
1309
|
}
|
|
1566
1310
|
validation = addDescription(validation, schema.description, this.context.useDescribe);
|
|
1567
1311
|
}
|
|
1568
1312
|
break;
|
|
1313
|
+
case void 0:
|
|
1569
1314
|
default:
|
|
1570
1315
|
validation = "z.unknown()";
|
|
1571
1316
|
validation = addDescription(validation, schema.description, this.context.useDescribe);
|
|
@@ -1624,194 +1369,50 @@ _PropertyGenerator.INCLUSION_RULES = {
|
|
|
1624
1369
|
};
|
|
1625
1370
|
var PropertyGenerator = _PropertyGenerator;
|
|
1626
1371
|
|
|
1627
|
-
// src/
|
|
1628
|
-
var
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
if (!value) {
|
|
1645
|
-
return false;
|
|
1646
|
-
}
|
|
1647
|
-
return patterns.some((pattern) => (0, import_minimatch2.minimatch)(value, pattern));
|
|
1648
|
-
}
|
|
1649
|
-
function containsAny(arr, values) {
|
|
1650
|
-
if (!values || values.length === 0) {
|
|
1651
|
-
return false;
|
|
1372
|
+
// src/openapi-generator.ts
|
|
1373
|
+
var import_node_fs = require("fs");
|
|
1374
|
+
var import_node_path = require("path");
|
|
1375
|
+
var import_openapi_core6 = require("@cerios/openapi-core");
|
|
1376
|
+
var import_openapi_to_typescript = require("@cerios/openapi-to-typescript");
|
|
1377
|
+
var import_minimatch = require("minimatch");
|
|
1378
|
+
|
|
1379
|
+
// src/generators/enum-generator.ts
|
|
1380
|
+
var import_openapi_core5 = require("@cerios/openapi-core");
|
|
1381
|
+
function generateEnum(name, values, options) {
|
|
1382
|
+
const schemaName = `${(0, import_openapi_core5.toCamelCase)(name, options)}Schema`;
|
|
1383
|
+
const typeName = (0, import_openapi_core5.toPascalCase)(name);
|
|
1384
|
+
const allBooleans = values.every((v) => typeof v === "boolean");
|
|
1385
|
+
if (allBooleans) {
|
|
1386
|
+
const schemaCode2 = `export const ${schemaName} = z.boolean();`;
|
|
1387
|
+
const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
|
|
1388
|
+
return { schemaCode: schemaCode2, typeCode: typeCode2 };
|
|
1652
1389
|
}
|
|
1653
|
-
|
|
1654
|
-
|
|
1390
|
+
const allStrings = values.every((v) => typeof v === "string");
|
|
1391
|
+
if (allStrings) {
|
|
1392
|
+
const enumValues = values.map((v) => `"${v}"`).join(", ");
|
|
1393
|
+
const schemaCode2 = `export const ${schemaName} = z.enum([${enumValues}]);`;
|
|
1394
|
+
const typeCode2 = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
|
|
1395
|
+
return { schemaCode: schemaCode2, typeCode: typeCode2 };
|
|
1655
1396
|
}
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
if (!filters) {
|
|
1660
|
-
return true;
|
|
1661
|
-
}
|
|
1662
|
-
const methodLower = method.toLowerCase();
|
|
1663
|
-
const operationId = operation == null ? void 0 : operation.operationId;
|
|
1664
|
-
const tags = (operation == null ? void 0 : operation.tags) || [];
|
|
1665
|
-
const deprecated = (operation == null ? void 0 : operation.deprecated) === true;
|
|
1666
|
-
if (filters.includeTags && filters.includeTags.length > 0) {
|
|
1667
|
-
if (!containsAny(tags, filters.includeTags)) {
|
|
1668
|
-
if (stats) stats.filteredByTags++;
|
|
1669
|
-
return false;
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
1672
|
-
if (filters.includePaths && filters.includePaths.length > 0) {
|
|
1673
|
-
if (!matchesAnyPattern(path, filters.includePaths)) {
|
|
1674
|
-
if (stats) stats.filteredByPaths++;
|
|
1675
|
-
return false;
|
|
1676
|
-
}
|
|
1677
|
-
}
|
|
1678
|
-
if (filters.includeMethods && filters.includeMethods.length > 0) {
|
|
1679
|
-
const methodsLower = filters.includeMethods.map((m) => m.toLowerCase());
|
|
1680
|
-
if (!methodsLower.includes(methodLower)) {
|
|
1681
|
-
if (stats) stats.filteredByMethods++;
|
|
1682
|
-
return false;
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
if (filters.includeOperationIds && filters.includeOperationIds.length > 0) {
|
|
1686
|
-
if (!matchesAnyPattern(operationId, filters.includeOperationIds)) {
|
|
1687
|
-
if (stats) stats.filteredByOperationIds++;
|
|
1688
|
-
return false;
|
|
1689
|
-
}
|
|
1690
|
-
}
|
|
1691
|
-
if (filters.excludeDeprecated === true && deprecated) {
|
|
1692
|
-
if (stats) stats.filteredByDeprecated++;
|
|
1693
|
-
return false;
|
|
1694
|
-
}
|
|
1695
|
-
if (filters.excludeTags && filters.excludeTags.length > 0) {
|
|
1696
|
-
if (containsAny(tags, filters.excludeTags)) {
|
|
1697
|
-
if (stats) stats.filteredByTags++;
|
|
1698
|
-
return false;
|
|
1699
|
-
}
|
|
1700
|
-
}
|
|
1701
|
-
if (filters.excludePaths && filters.excludePaths.length > 0) {
|
|
1702
|
-
if (matchesAnyPattern(path, filters.excludePaths)) {
|
|
1703
|
-
if (stats) stats.filteredByPaths++;
|
|
1704
|
-
return false;
|
|
1705
|
-
}
|
|
1706
|
-
}
|
|
1707
|
-
if (filters.excludeMethods && filters.excludeMethods.length > 0) {
|
|
1708
|
-
const methodsLower = filters.excludeMethods.map((m) => m.toLowerCase());
|
|
1709
|
-
if (methodsLower.includes(methodLower)) {
|
|
1710
|
-
if (stats) stats.filteredByMethods++;
|
|
1711
|
-
return false;
|
|
1712
|
-
}
|
|
1713
|
-
}
|
|
1714
|
-
if (filters.excludeOperationIds && filters.excludeOperationIds.length > 0) {
|
|
1715
|
-
if (matchesAnyPattern(operationId, filters.excludeOperationIds)) {
|
|
1716
|
-
if (stats) stats.filteredByOperationIds++;
|
|
1717
|
-
return false;
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
return true;
|
|
1721
|
-
}
|
|
1722
|
-
function validateFilters(stats, filters) {
|
|
1723
|
-
if (!filters || stats.totalOperations === 0) {
|
|
1724
|
-
return;
|
|
1725
|
-
}
|
|
1726
|
-
if (stats.includedOperations === 0) {
|
|
1727
|
-
console.warn(
|
|
1728
|
-
`\u26A0\uFE0F Warning: All ${stats.totalOperations} operations were filtered out. Check your operationFilters configuration.`
|
|
1729
|
-
);
|
|
1730
|
-
const filterBreakdown = [];
|
|
1731
|
-
if (stats.filteredByTags > 0) filterBreakdown.push(`${stats.filteredByTags} by tags`);
|
|
1732
|
-
if (stats.filteredByPaths > 0) filterBreakdown.push(`${stats.filteredByPaths} by paths`);
|
|
1733
|
-
if (stats.filteredByMethods > 0) filterBreakdown.push(`${stats.filteredByMethods} by methods`);
|
|
1734
|
-
if (stats.filteredByOperationIds > 0) filterBreakdown.push(`${stats.filteredByOperationIds} by operationIds`);
|
|
1735
|
-
if (stats.filteredByDeprecated > 0) filterBreakdown.push(`${stats.filteredByDeprecated} by deprecated flag`);
|
|
1736
|
-
if (filterBreakdown.length > 0) {
|
|
1737
|
-
console.warn(` Filtered: ${filterBreakdown.join(", ")}`);
|
|
1397
|
+
const literalValues = values.map((v) => {
|
|
1398
|
+
if (typeof v === "string") {
|
|
1399
|
+
return `z.literal("${v}")`;
|
|
1738
1400
|
}
|
|
1739
|
-
|
|
1740
|
-
}
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
}
|
|
1745
|
-
const lines = [];
|
|
1746
|
-
lines.push("Operation Filtering:");
|
|
1747
|
-
lines.push(` Total operations: ${stats.totalOperations}`);
|
|
1748
|
-
lines.push(` Included operations: ${stats.includedOperations}`);
|
|
1749
|
-
const filteredCount = stats.filteredByTags + stats.filteredByPaths + stats.filteredByMethods + stats.filteredByOperationIds + stats.filteredByDeprecated;
|
|
1750
|
-
if (filteredCount > 0) {
|
|
1751
|
-
lines.push(` Filtered operations: ${filteredCount}`);
|
|
1752
|
-
if (stats.filteredByTags > 0) lines.push(` - By tags: ${stats.filteredByTags}`);
|
|
1753
|
-
if (stats.filteredByPaths > 0) lines.push(` - By paths: ${stats.filteredByPaths}`);
|
|
1754
|
-
if (stats.filteredByMethods > 0) lines.push(` - By methods: ${stats.filteredByMethods}`);
|
|
1755
|
-
if (stats.filteredByOperationIds > 0) lines.push(` - By operationIds: ${stats.filteredByOperationIds}`);
|
|
1756
|
-
if (stats.filteredByDeprecated > 0) lines.push(` - By deprecated: ${stats.filteredByDeprecated}`);
|
|
1757
|
-
}
|
|
1758
|
-
return lines.join("\n");
|
|
1401
|
+
return `z.literal(${v})`;
|
|
1402
|
+
}).join(", ");
|
|
1403
|
+
const schemaCode = `export const ${schemaName} = z.union([${literalValues}]);`;
|
|
1404
|
+
const typeCode = `export type ${typeName} = z.infer<typeof ${schemaName}>;`;
|
|
1405
|
+
return { schemaCode, typeCode };
|
|
1759
1406
|
}
|
|
1760
1407
|
|
|
1761
|
-
// src/
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
if (!obj.$ref) return obj;
|
|
1766
|
-
const ref = obj.$ref;
|
|
1767
|
-
let resolved = null;
|
|
1768
|
-
const paramMatch = ref.match(/^#\/components\/parameters\/(.+)$/);
|
|
1769
|
-
const requestBodyMatch = ref.match(/^#\/components\/requestBodies\/(.+)$/);
|
|
1770
|
-
const responseMatch = ref.match(/^#\/components\/responses\/(.+)$/);
|
|
1771
|
-
const schemaMatch = ref.match(/^#\/components\/schemas\/(.+)$/);
|
|
1772
|
-
if (paramMatch && ((_a = spec.components) == null ? void 0 : _a.parameters)) {
|
|
1773
|
-
const name = paramMatch[1];
|
|
1774
|
-
resolved = spec.components.parameters[name];
|
|
1775
|
-
} else if (requestBodyMatch && ((_b = spec.components) == null ? void 0 : _b.requestBodies)) {
|
|
1776
|
-
const name = requestBodyMatch[1];
|
|
1777
|
-
resolved = spec.components.requestBodies[name];
|
|
1778
|
-
} else if (responseMatch && ((_c = spec.components) == null ? void 0 : _c.responses)) {
|
|
1779
|
-
const name = responseMatch[1];
|
|
1780
|
-
resolved = spec.components.responses[name];
|
|
1781
|
-
} else if (schemaMatch && ((_d = spec.components) == null ? void 0 : _d.schemas)) {
|
|
1782
|
-
const name = schemaMatch[1];
|
|
1783
|
-
resolved = spec.components.schemas[name];
|
|
1784
|
-
}
|
|
1785
|
-
if (resolved) {
|
|
1786
|
-
if (resolved.$ref) {
|
|
1787
|
-
return resolveRef2(resolved, spec, maxDepth - 1);
|
|
1788
|
-
}
|
|
1789
|
-
return resolved;
|
|
1790
|
-
}
|
|
1791
|
-
return obj;
|
|
1408
|
+
// src/openapi-generator.ts
|
|
1409
|
+
var HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options"];
|
|
1410
|
+
function isResolvedParameter(param) {
|
|
1411
|
+
return typeof param === "object" && param !== null && "name" in param && typeof param.name === "string" && "in" in param;
|
|
1792
1412
|
}
|
|
1793
|
-
function
|
|
1794
|
-
return
|
|
1413
|
+
function isOpenAPIPathItem(value) {
|
|
1414
|
+
return typeof value === "object" && value !== null;
|
|
1795
1415
|
}
|
|
1796
|
-
function mergeParameters(pathParams, operationParams, spec) {
|
|
1797
|
-
const resolvedPathParams = (pathParams || []).map((p) => resolveParameterRef(p, spec));
|
|
1798
|
-
const resolvedOperationParams = (operationParams || []).map((p) => resolveParameterRef(p, spec));
|
|
1799
|
-
const merged = [...resolvedPathParams];
|
|
1800
|
-
for (const opParam of resolvedOperationParams) {
|
|
1801
|
-
if (!opParam || typeof opParam !== "object") continue;
|
|
1802
|
-
const existingIndex = merged.findIndex(
|
|
1803
|
-
(p) => p && typeof p === "object" && p.name === opParam.name && p.in === opParam.in
|
|
1804
|
-
);
|
|
1805
|
-
if (existingIndex >= 0) {
|
|
1806
|
-
merged[existingIndex] = opParam;
|
|
1807
|
-
} else {
|
|
1808
|
-
merged.push(opParam);
|
|
1809
|
-
}
|
|
1810
|
-
}
|
|
1811
|
-
return merged;
|
|
1812
|
-
}
|
|
1813
|
-
|
|
1814
|
-
// src/openapi-generator.ts
|
|
1815
1416
|
var OpenApiGenerator = class {
|
|
1816
1417
|
constructor(options) {
|
|
1817
1418
|
this.schemas = /* @__PURE__ */ new Map();
|
|
@@ -1819,94 +1420,49 @@ var OpenApiGenerator = class {
|
|
|
1819
1420
|
this.schemaDependencies = /* @__PURE__ */ new Map();
|
|
1820
1421
|
this.schemaUsageMap = /* @__PURE__ */ new Map();
|
|
1821
1422
|
this.needsZodImport = true;
|
|
1822
|
-
this.filterStats = createFilterStatistics();
|
|
1823
|
-
|
|
1423
|
+
this.filterStats = (0, import_openapi_core6.createFilterStatistics)();
|
|
1424
|
+
/** Track total allOf conflicts detected across all schemas */
|
|
1425
|
+
this.allOfConflictCount = 0;
|
|
1426
|
+
/** Track schemas involved in circular dependency chains */
|
|
1427
|
+
this.circularDependencies = /* @__PURE__ */ new Set();
|
|
1428
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
1824
1429
|
if (!options.input) {
|
|
1825
|
-
throw new ConfigurationError("Input path is required", { providedOptions: options });
|
|
1430
|
+
throw new import_openapi_core6.ConfigurationError("Input path is required", { providedOptions: options });
|
|
1826
1431
|
}
|
|
1432
|
+
this.separateSchemasMode = Boolean(options.outputZodSchemas);
|
|
1827
1433
|
this.options = {
|
|
1828
1434
|
mode: options.mode || "normal",
|
|
1829
1435
|
input: options.input,
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1436
|
+
outputTypes: options.outputTypes,
|
|
1437
|
+
outputZodSchemas: options.outputZodSchemas,
|
|
1438
|
+
enumFormat: options.enumFormat,
|
|
1439
|
+
typeAssertionThreshold: (_a = options.typeAssertionThreshold) != null ? _a : 0,
|
|
1440
|
+
includeDescriptions: (_b = options.includeDescriptions) != null ? _b : true,
|
|
1441
|
+
useDescribe: (_c = options.useDescribe) != null ? _c : false,
|
|
1442
|
+
defaultNullable: (_d = options.defaultNullable) != null ? _d : false,
|
|
1443
|
+
emptyObjectBehavior: (_e = options.emptyObjectBehavior) != null ? _e : "loose",
|
|
1835
1444
|
schemaType: options.schemaType || "all",
|
|
1836
1445
|
prefix: options.prefix,
|
|
1837
1446
|
suffix: options.suffix,
|
|
1838
1447
|
stripSchemaPrefix: options.stripSchemaPrefix,
|
|
1839
1448
|
stripPathPrefix: options.stripPathPrefix,
|
|
1840
|
-
|
|
1449
|
+
useOperationId: (_f = options.useOperationId) != null ? _f : true,
|
|
1450
|
+
showStats: (_g = options.showStats) != null ? _g : true,
|
|
1841
1451
|
request: options.request,
|
|
1842
1452
|
response: options.response,
|
|
1843
1453
|
operationFilters: options.operationFilters,
|
|
1844
1454
|
ignoreHeaders: options.ignoreHeaders,
|
|
1845
|
-
cacheSize: (
|
|
1846
|
-
batchSize: (
|
|
1455
|
+
cacheSize: (_h = options.cacheSize) != null ? _h : 1e3,
|
|
1456
|
+
batchSize: (_i = options.batchSize) != null ? _i : 10,
|
|
1847
1457
|
customDateTimeFormatRegex: options.customDateTimeFormatRegex
|
|
1848
1458
|
};
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
if (this.options.customDateTimeFormatRegex) {
|
|
1853
|
-
configureDateTimeFormat(this.options.customDateTimeFormatRegex);
|
|
1854
|
-
}
|
|
1855
|
-
try {
|
|
1856
|
-
const fs = require("fs");
|
|
1857
|
-
if (!fs.existsSync(this.options.input)) {
|
|
1858
|
-
throw new FileOperationError(`Input file not found: ${this.options.input}`, this.options.input);
|
|
1859
|
-
}
|
|
1860
|
-
} catch (error) {
|
|
1861
|
-
if (error instanceof FileOperationError) {
|
|
1862
|
-
throw error;
|
|
1863
|
-
}
|
|
1864
|
-
}
|
|
1865
|
-
try {
|
|
1866
|
-
const content = (0, import_node_fs.readFileSync)(this.options.input, "utf-8");
|
|
1867
|
-
try {
|
|
1868
|
-
this.spec = (0, import_yaml.parse)(content);
|
|
1869
|
-
} catch (yamlError) {
|
|
1870
|
-
try {
|
|
1871
|
-
this.spec = JSON.parse(content);
|
|
1872
|
-
} catch {
|
|
1873
|
-
if (yamlError instanceof Error) {
|
|
1874
|
-
const errorMessage = [
|
|
1875
|
-
`Failed to parse OpenAPI specification from: ${this.options.input}`,
|
|
1876
|
-
"",
|
|
1877
|
-
`Error: ${yamlError.message}`,
|
|
1878
|
-
"",
|
|
1879
|
-
"Please ensure:",
|
|
1880
|
-
" - The file exists and is readable",
|
|
1881
|
-
" - The file contains valid YAML or JSON syntax",
|
|
1882
|
-
" - The file is a valid OpenAPI 3.x specification"
|
|
1883
|
-
].join("\n");
|
|
1884
|
-
throw new SpecValidationError(errorMessage, {
|
|
1885
|
-
filePath: this.options.input,
|
|
1886
|
-
originalError: yamlError.message
|
|
1887
|
-
});
|
|
1888
|
-
}
|
|
1889
|
-
throw yamlError;
|
|
1890
|
-
}
|
|
1891
|
-
}
|
|
1892
|
-
} catch (error) {
|
|
1893
|
-
if (error instanceof SpecValidationError) {
|
|
1894
|
-
throw error;
|
|
1895
|
-
}
|
|
1896
|
-
if (error instanceof Error) {
|
|
1897
|
-
const errorMessage = [
|
|
1898
|
-
`Failed to read OpenAPI specification from: ${this.options.input}`,
|
|
1899
|
-
"",
|
|
1900
|
-
`Error: ${error.message}`
|
|
1901
|
-
].join("\n");
|
|
1902
|
-
throw new SpecValidationError(errorMessage, { filePath: this.options.input, originalError: error.message });
|
|
1903
|
-
}
|
|
1904
|
-
throw error;
|
|
1905
|
-
}
|
|
1459
|
+
this.patternCache = new import_openapi_core6.LRUCache((_j = this.options.cacheSize) != null ? _j : 1e3);
|
|
1460
|
+
this.dateTimeValidation = buildDateTimeValidation(this.options.customDateTimeFormatRegex);
|
|
1461
|
+
this.spec = (0, import_openapi_core6.loadOpenAPISpec)(this.options.input);
|
|
1906
1462
|
this.validateSpec();
|
|
1907
1463
|
this.requestOptions = this.resolveOptionsForContext("request");
|
|
1908
1464
|
this.responseOptions = this.resolveOptionsForContext("response");
|
|
1909
|
-
this.
|
|
1465
|
+
this.initializeSchemaUsage();
|
|
1910
1466
|
this.propertyGenerator = new PropertyGenerator({
|
|
1911
1467
|
spec: this.spec,
|
|
1912
1468
|
schemaDependencies: this.schemaDependencies,
|
|
@@ -1914,24 +1470,33 @@ var OpenApiGenerator = class {
|
|
|
1914
1470
|
mode: this.requestOptions.mode,
|
|
1915
1471
|
includeDescriptions: this.requestOptions.includeDescriptions,
|
|
1916
1472
|
useDescribe: this.requestOptions.useDescribe,
|
|
1917
|
-
defaultNullable: (
|
|
1918
|
-
emptyObjectBehavior: (
|
|
1473
|
+
defaultNullable: (_k = this.options.defaultNullable) != null ? _k : false,
|
|
1474
|
+
emptyObjectBehavior: (_l = this.options.emptyObjectBehavior) != null ? _l : "loose",
|
|
1919
1475
|
namingOptions: {
|
|
1920
1476
|
prefix: this.options.prefix,
|
|
1921
1477
|
suffix: this.options.suffix
|
|
1922
1478
|
},
|
|
1923
|
-
stripSchemaPrefix: this.options.stripSchemaPrefix
|
|
1479
|
+
stripSchemaPrefix: this.options.stripSchemaPrefix,
|
|
1480
|
+
dateTimeValidation: this.dateTimeValidation,
|
|
1481
|
+
patternCache: this.patternCache,
|
|
1482
|
+
separateTypesFile: this.separateSchemasMode
|
|
1924
1483
|
});
|
|
1925
1484
|
}
|
|
1926
1485
|
/**
|
|
1927
1486
|
* Generate schemas as a string (without writing to file)
|
|
1487
|
+
* When separateSchemasMode is active, generates Zod schemas with explicit type annotations
|
|
1928
1488
|
* @returns The generated TypeScript code as a string
|
|
1929
1489
|
*/
|
|
1930
1490
|
generateString() {
|
|
1931
1491
|
var _a;
|
|
1932
1492
|
if (!((_a = this.spec.components) == null ? void 0 : _a.schemas)) {
|
|
1933
|
-
throw new SpecValidationError("No schemas found in OpenAPI spec", { filePath: this.options.input });
|
|
1493
|
+
throw new import_openapi_core6.SpecValidationError("No schemas found in OpenAPI spec", { filePath: this.options.input });
|
|
1934
1494
|
}
|
|
1495
|
+
if (this.separateSchemasMode) {
|
|
1496
|
+
return this.generateSeparateSchemasString();
|
|
1497
|
+
}
|
|
1498
|
+
this.analyzeCircularDependencies();
|
|
1499
|
+
this.propertyGenerator.setCircularDependencies(this.circularDependencies);
|
|
1935
1500
|
for (const [name, schema] of Object.entries(this.spec.components.schemas)) {
|
|
1936
1501
|
if (this.options.operationFilters && this.schemaUsageMap.size > 0 && !this.schemaUsageMap.has(name)) {
|
|
1937
1502
|
continue;
|
|
@@ -1940,7 +1505,7 @@ var OpenApiGenerator = class {
|
|
|
1940
1505
|
}
|
|
1941
1506
|
this.generateQueryParameterSchemas();
|
|
1942
1507
|
this.generateHeaderParameterSchemas();
|
|
1943
|
-
validateFilters(this.filterStats, this.options.operationFilters);
|
|
1508
|
+
(0, import_openapi_core6.validateFilters)(this.filterStats, this.options.operationFilters);
|
|
1944
1509
|
const orderedSchemaNames = this.topologicalSort();
|
|
1945
1510
|
const output = ["// Auto-generated by @cerios/openapi-to-zod", "// Do not edit this file manually", ""];
|
|
1946
1511
|
if (this.options.showStats === true) {
|
|
@@ -1957,10 +1522,10 @@ var OpenApiGenerator = class {
|
|
|
1957
1522
|
const typeCode = this.types.get(name);
|
|
1958
1523
|
if (schemaCode) {
|
|
1959
1524
|
output.push(schemaCode);
|
|
1960
|
-
const strippedName = stripPrefix(name, this.options.stripSchemaPrefix);
|
|
1961
|
-
const typeName = toPascalCase(strippedName);
|
|
1525
|
+
const strippedName = (0, import_openapi_core6.stripPrefix)(name, this.options.stripSchemaPrefix);
|
|
1526
|
+
const typeName = (0, import_openapi_core6.toPascalCase)(strippedName);
|
|
1962
1527
|
if (!schemaCode.includes(`export type ${typeName}`)) {
|
|
1963
|
-
const schemaName = `${toCamelCase(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
|
|
1528
|
+
const schemaName = `${(0, import_openapi_core6.toCamelCase)(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
|
|
1964
1529
|
output.push(`export type ${typeName} = z.infer<typeof ${schemaName}>;`);
|
|
1965
1530
|
}
|
|
1966
1531
|
output.push("");
|
|
@@ -1982,14 +1547,224 @@ var OpenApiGenerator = class {
|
|
|
1982
1547
|
}
|
|
1983
1548
|
}
|
|
1984
1549
|
/**
|
|
1985
|
-
* Generate the complete output file
|
|
1550
|
+
* Generate the complete output file(s)
|
|
1551
|
+
* When separateSchemasMode is active, generates both types and schemas files
|
|
1986
1552
|
*/
|
|
1987
1553
|
generate() {
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1554
|
+
if (this.separateSchemasMode) {
|
|
1555
|
+
const typesContent = this.generateTypesString();
|
|
1556
|
+
const schemasContent = this.generateString();
|
|
1557
|
+
const normalizedTypes = (0, import_node_path.normalize)(this.options.outputTypes);
|
|
1558
|
+
this.ensureDirectoryExists(normalizedTypes);
|
|
1559
|
+
(0, import_node_fs.writeFileSync)(normalizedTypes, typesContent, "utf-8");
|
|
1560
|
+
console.log(` \u2713 Generated ${normalizedTypes}`);
|
|
1561
|
+
if (!this.options.outputZodSchemas) {
|
|
1562
|
+
throw new Error("Internal error: outputZodSchemas should be defined in separateSchemasMode");
|
|
1563
|
+
}
|
|
1564
|
+
const outputZodSchemas = this.options.outputZodSchemas;
|
|
1565
|
+
const normalizedSchemas = (0, import_node_path.normalize)(outputZodSchemas);
|
|
1566
|
+
this.ensureDirectoryExists(normalizedSchemas);
|
|
1567
|
+
(0, import_node_fs.writeFileSync)(normalizedSchemas, schemasContent, "utf-8");
|
|
1568
|
+
console.log(` \u2713 Generated ${normalizedSchemas}`);
|
|
1569
|
+
} else {
|
|
1570
|
+
const output = this.generateString();
|
|
1571
|
+
const normalizedOutput = (0, import_node_path.normalize)(this.options.outputTypes);
|
|
1572
|
+
this.ensureDirectoryExists(normalizedOutput);
|
|
1573
|
+
(0, import_node_fs.writeFileSync)(normalizedOutput, output);
|
|
1574
|
+
console.log(` \u2713 Generated ${normalizedOutput}`);
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
/**
|
|
1578
|
+
* Generate Zod schemas with explicit type annotations (for outputZodSchemas mode)
|
|
1579
|
+
* Generates schemas like: `export const userSchema: z.ZodType<User> = z.object({...})`
|
|
1580
|
+
* @returns The generated Zod schemas TypeScript code
|
|
1581
|
+
*/
|
|
1582
|
+
generateSeparateSchemasString() {
|
|
1583
|
+
var _a;
|
|
1584
|
+
const schemas = (_a = this.spec.components) == null ? void 0 : _a.schemas;
|
|
1585
|
+
if (!schemas) {
|
|
1586
|
+
return "";
|
|
1587
|
+
}
|
|
1588
|
+
if (!this.options.outputZodSchemas) {
|
|
1589
|
+
throw new Error("Internal error: outputZodSchemas should be defined in separateSchemasMode");
|
|
1590
|
+
}
|
|
1591
|
+
const outputZodSchemas = this.options.outputZodSchemas;
|
|
1592
|
+
this.analyzeCircularDependencies();
|
|
1593
|
+
this.propertyGenerator.setCircularDependencies(this.circularDependencies);
|
|
1594
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
1595
|
+
if (this.options.operationFilters && this.schemaUsageMap.size > 0 && !this.schemaUsageMap.has(name)) {
|
|
1596
|
+
continue;
|
|
1597
|
+
}
|
|
1598
|
+
this.generateComponentSchema(name, schema);
|
|
1599
|
+
}
|
|
1600
|
+
this.generateQueryParameterSchemas();
|
|
1601
|
+
this.generateHeaderParameterSchemas();
|
|
1602
|
+
(0, import_openapi_core6.validateFilters)(this.filterStats, this.options.operationFilters);
|
|
1603
|
+
const orderedSchemaNames = this.topologicalSort();
|
|
1604
|
+
const output = ["// Auto-generated by @cerios/openapi-to-zod", "// Do not edit this file manually", ""];
|
|
1605
|
+
if (this.options.showStats === true) {
|
|
1606
|
+
output.push(...this.generateStats());
|
|
1607
|
+
output.push("");
|
|
1608
|
+
}
|
|
1609
|
+
output.push('import { z } from "zod";');
|
|
1610
|
+
const typesImportPath = this.calculateRelativeImportPath(outputZodSchemas, this.options.outputTypes);
|
|
1611
|
+
const typeNames = [];
|
|
1612
|
+
for (const name of orderedSchemaNames) {
|
|
1613
|
+
const strippedName = (0, import_openapi_core6.stripPrefix)(name, this.options.stripSchemaPrefix);
|
|
1614
|
+
const typeName = (0, import_openapi_core6.toPascalCase)(strippedName);
|
|
1615
|
+
typeNames.push(typeName);
|
|
1616
|
+
}
|
|
1617
|
+
if (typeNames.length > 0) {
|
|
1618
|
+
output.push(`import type { ${typeNames.join(", ")} } from "${typesImportPath}";`);
|
|
1619
|
+
}
|
|
1620
|
+
output.push("");
|
|
1621
|
+
output.push("// Schemas");
|
|
1622
|
+
for (const name of orderedSchemaNames) {
|
|
1623
|
+
const schemaCode = this.schemas.get(name);
|
|
1624
|
+
if (schemaCode) {
|
|
1625
|
+
const strippedName = (0, import_openapi_core6.stripPrefix)(name, this.options.stripSchemaPrefix);
|
|
1626
|
+
const typeName = (0, import_openapi_core6.toPascalCase)(strippedName);
|
|
1627
|
+
const schemaName = `${(0, import_openapi_core6.toCamelCase)(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
|
|
1628
|
+
const schemaDefinition = this.isRecordObject(schemas[name]) ? schemas[name] : void 0;
|
|
1629
|
+
const transformedCode = this.addExplicitTypeAnnotation(schemaCode, schemaName, typeName, schemaDefinition);
|
|
1630
|
+
output.push(transformedCode);
|
|
1631
|
+
output.push("");
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
return output.join("\n");
|
|
1635
|
+
}
|
|
1636
|
+
/**
|
|
1637
|
+
* Generate TypeScript types as a string (for outputZodSchemas mode)
|
|
1638
|
+
* Uses @cerios/openapi-to-typescript internally
|
|
1639
|
+
* @returns The generated TypeScript types code
|
|
1640
|
+
*/
|
|
1641
|
+
generateTypesString() {
|
|
1642
|
+
var _a;
|
|
1643
|
+
const tsGenerator = new import_openapi_to_typescript.TypeScriptGenerator({
|
|
1644
|
+
input: this.options.input,
|
|
1645
|
+
outputTypes: this.options.outputTypes,
|
|
1646
|
+
includeDescriptions: this.options.includeDescriptions,
|
|
1647
|
+
defaultNullable: this.options.defaultNullable,
|
|
1648
|
+
prefix: this.options.prefix,
|
|
1649
|
+
suffix: this.options.suffix,
|
|
1650
|
+
stripSchemaPrefix: this.options.stripSchemaPrefix,
|
|
1651
|
+
stripPathPrefix: this.options.stripPathPrefix,
|
|
1652
|
+
operationFilters: this.options.operationFilters,
|
|
1653
|
+
showStats: this.options.showStats,
|
|
1654
|
+
enumFormat: (_a = this.options.enumFormat) != null ? _a : "const-object"
|
|
1655
|
+
});
|
|
1656
|
+
return tsGenerator.generateString();
|
|
1657
|
+
}
|
|
1658
|
+
/**
|
|
1659
|
+
* Add explicit type annotation to a schema declaration
|
|
1660
|
+
* Transforms: `export const userSchema = z.object({...})`
|
|
1661
|
+
* To: `export const userSchema: z.ZodType<User> = z.object({...})` (annotation)
|
|
1662
|
+
* Or: `export const userSchema = z.object({...}) as unknown as z.ZodType<User>` (double assertion)
|
|
1663
|
+
*
|
|
1664
|
+
* Uses double assertion via `unknown` when typeAssertionThreshold is set and schema complexity
|
|
1665
|
+
* meets or exceeds the threshold. This completely bypasses TypeScript's structural checking
|
|
1666
|
+
* to avoid "Type instantiation is excessively deep" errors on very large schemas.
|
|
1667
|
+
*
|
|
1668
|
+
* Also removes any `export type X = z.infer<...>` lines since types
|
|
1669
|
+
* are imported from the separate types file.
|
|
1670
|
+
*/
|
|
1671
|
+
addExplicitTypeAnnotation(schemaCode, schemaName, typeName, schemaDefinition) {
|
|
1672
|
+
var _a;
|
|
1673
|
+
const code = schemaCode.replace(/\nexport type \w+ = z\.infer<typeof \w+>;/g, "");
|
|
1674
|
+
const jsdocMatch = code.match(/^(\/\*\*[\s\S]*?\*\/\n)?/);
|
|
1675
|
+
const jsdoc = (jsdocMatch == null ? void 0 : jsdocMatch[1]) || "";
|
|
1676
|
+
const codeWithoutJsdoc = code.slice(jsdoc.length);
|
|
1677
|
+
const threshold = (_a = this.options.typeAssertionThreshold) != null ? _a : 0;
|
|
1678
|
+
const useAssertion = threshold > 0 && schemaDefinition && this.calculateSchemaComplexity(schemaDefinition) >= threshold;
|
|
1679
|
+
const pattern = new RegExp(`export const ${schemaName} = `);
|
|
1680
|
+
if (pattern.test(codeWithoutJsdoc)) {
|
|
1681
|
+
let schemaBody = codeWithoutJsdoc.replace(pattern, "");
|
|
1682
|
+
if (useAssertion) {
|
|
1683
|
+
schemaBody = schemaBody.replace(/;$/, "");
|
|
1684
|
+
return `${jsdoc}export const ${schemaName} = ${schemaBody} as unknown as z.ZodType<${typeName}>;`;
|
|
1685
|
+
}
|
|
1686
|
+
return `${jsdoc}export const ${schemaName}: z.ZodType<${typeName}> = ${schemaBody}`;
|
|
1687
|
+
}
|
|
1688
|
+
return code;
|
|
1689
|
+
}
|
|
1690
|
+
/**
|
|
1691
|
+
* Type guard to check if a value is a Record<string, unknown>
|
|
1692
|
+
*/
|
|
1693
|
+
isRecordObject(value) {
|
|
1694
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1695
|
+
}
|
|
1696
|
+
/**
|
|
1697
|
+
* Calculate the complexity of a schema for threshold comparison
|
|
1698
|
+
* Complexity formula: properties + (nested levels * 10) + (array/union members * 2)
|
|
1699
|
+
*/
|
|
1700
|
+
calculateSchemaComplexity(schema, depth = 0) {
|
|
1701
|
+
if (!schema || typeof schema !== "object") {
|
|
1702
|
+
return 0;
|
|
1703
|
+
}
|
|
1704
|
+
let complexity = depth * 10;
|
|
1705
|
+
if (schema.$ref) {
|
|
1706
|
+
return complexity + 5;
|
|
1707
|
+
}
|
|
1708
|
+
const properties = schema.properties;
|
|
1709
|
+
if (this.isRecordObject(properties)) {
|
|
1710
|
+
const propCount = Object.keys(properties).length;
|
|
1711
|
+
complexity += propCount;
|
|
1712
|
+
for (const prop of Object.values(properties)) {
|
|
1713
|
+
if (this.isRecordObject(prop)) {
|
|
1714
|
+
complexity += this.calculateSchemaComplexity(prop, depth + 1);
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
const allOf = schema.allOf;
|
|
1719
|
+
if (Array.isArray(allOf)) {
|
|
1720
|
+
complexity += allOf.length * 2;
|
|
1721
|
+
for (const subSchema of allOf) {
|
|
1722
|
+
if (this.isRecordObject(subSchema)) {
|
|
1723
|
+
complexity += this.calculateSchemaComplexity(subSchema, depth + 1);
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
const oneOf = schema.oneOf;
|
|
1728
|
+
if (Array.isArray(oneOf)) {
|
|
1729
|
+
complexity += oneOf.length * 2;
|
|
1730
|
+
for (const subSchema of oneOf) {
|
|
1731
|
+
if (this.isRecordObject(subSchema)) {
|
|
1732
|
+
complexity += this.calculateSchemaComplexity(subSchema, depth + 1);
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
const anyOf = schema.anyOf;
|
|
1737
|
+
if (Array.isArray(anyOf)) {
|
|
1738
|
+
complexity += anyOf.length * 2;
|
|
1739
|
+
for (const subSchema of anyOf) {
|
|
1740
|
+
if (this.isRecordObject(subSchema)) {
|
|
1741
|
+
complexity += this.calculateSchemaComplexity(subSchema, depth + 1);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
const items = schema.items;
|
|
1746
|
+
if (this.isRecordObject(items)) {
|
|
1747
|
+
complexity += 2;
|
|
1748
|
+
complexity += this.calculateSchemaComplexity(items, depth + 1);
|
|
1749
|
+
}
|
|
1750
|
+
const additionalProps = schema.additionalProperties;
|
|
1751
|
+
if (this.isRecordObject(additionalProps)) {
|
|
1752
|
+
complexity += 2;
|
|
1753
|
+
complexity += this.calculateSchemaComplexity(additionalProps, depth + 1);
|
|
1754
|
+
}
|
|
1755
|
+
return complexity;
|
|
1756
|
+
}
|
|
1757
|
+
/**
|
|
1758
|
+
* Calculate relative import path from schema file to types file
|
|
1759
|
+
*/
|
|
1760
|
+
calculateRelativeImportPath(fromPath, toPath) {
|
|
1761
|
+
const fromDir = (0, import_node_path.dirname)((0, import_node_path.normalize)(fromPath));
|
|
1762
|
+
const toFile = (0, import_node_path.normalize)(toPath).replace(/\.[tj]s$/, "");
|
|
1763
|
+
let relativePath = (0, import_node_path.relative)(fromDir, toFile);
|
|
1764
|
+
if (!relativePath.startsWith(".") && !relativePath.startsWith("..")) {
|
|
1765
|
+
relativePath = `./${relativePath}`;
|
|
1766
|
+
}
|
|
1767
|
+
return relativePath.replace(/\\/g, "/");
|
|
1993
1768
|
}
|
|
1994
1769
|
/**
|
|
1995
1770
|
* Resolve options for a specific context (request or response)
|
|
@@ -2005,191 +1780,84 @@ var OpenApiGenerator = class {
|
|
|
2005
1780
|
};
|
|
2006
1781
|
}
|
|
2007
1782
|
/**
|
|
2008
|
-
*
|
|
2009
|
-
*
|
|
1783
|
+
* Initialize schema usage map using core utilities with operation filtering
|
|
1784
|
+
* This is a wrapper around core's analyzeSchemaUsage that adds operation filtering
|
|
2010
1785
|
*/
|
|
2011
|
-
|
|
2012
|
-
var _a
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
1786
|
+
initializeSchemaUsage() {
|
|
1787
|
+
var _a;
|
|
1788
|
+
if (this.options.operationFilters && this.spec.paths) {
|
|
1789
|
+
const requestSchemas = /* @__PURE__ */ new Set();
|
|
1790
|
+
const responseSchemas = /* @__PURE__ */ new Set();
|
|
2016
1791
|
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
|
|
2017
|
-
|
|
2018
|
-
for (const method of
|
|
1792
|
+
if (!isOpenAPIPathItem(pathItem)) continue;
|
|
1793
|
+
for (const method of HTTP_METHODS) {
|
|
2019
1794
|
const operation = pathItem[method];
|
|
2020
|
-
if (
|
|
1795
|
+
if (!operation) continue;
|
|
2021
1796
|
this.filterStats.totalOperations++;
|
|
2022
|
-
if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters, this.filterStats)) {
|
|
1797
|
+
if (!(0, import_openapi_core6.shouldIncludeOperation)(operation, path, method, this.options.operationFilters, this.filterStats)) {
|
|
2023
1798
|
continue;
|
|
2024
1799
|
}
|
|
2025
1800
|
this.filterStats.includedOperations++;
|
|
2026
|
-
if (
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
1801
|
+
if (operation.requestBody && typeof operation.requestBody === "object") {
|
|
1802
|
+
const reqBody = operation.requestBody;
|
|
1803
|
+
if (reqBody.content && typeof reqBody.content === "object") {
|
|
1804
|
+
for (const mediaType of Object.values(reqBody.content)) {
|
|
1805
|
+
if (mediaType && typeof mediaType === "object" && "schema" in mediaType && mediaType.schema) {
|
|
1806
|
+
(0, import_openapi_core6.extractSchemaRefs)(mediaType.schema, requestSchemas);
|
|
1807
|
+
}
|
|
2030
1808
|
}
|
|
2031
1809
|
}
|
|
2032
1810
|
}
|
|
2033
|
-
if (
|
|
1811
|
+
if (operation.responses && typeof operation.responses === "object") {
|
|
2034
1812
|
for (const response of Object.values(operation.responses)) {
|
|
2035
1813
|
if (response && typeof response === "object" && "content" in response && response.content && typeof response.content === "object") {
|
|
2036
1814
|
for (const mediaType of Object.values(response.content)) {
|
|
2037
1815
|
if (mediaType && typeof mediaType === "object" && "schema" in mediaType && mediaType.schema) {
|
|
2038
|
-
|
|
1816
|
+
(0, import_openapi_core6.extractSchemaRefs)(mediaType.schema, responseSchemas);
|
|
2039
1817
|
}
|
|
2040
1818
|
}
|
|
2041
1819
|
}
|
|
2042
1820
|
}
|
|
2043
1821
|
}
|
|
2044
|
-
if (
|
|
1822
|
+
if (operation.parameters && Array.isArray(operation.parameters)) {
|
|
2045
1823
|
for (const param of operation.parameters) {
|
|
2046
|
-
if (param &&
|
|
2047
|
-
|
|
1824
|
+
if (isResolvedParameter(param) && param.schema) {
|
|
1825
|
+
(0, import_openapi_core6.extractSchemaRefs)(param.schema, requestSchemas);
|
|
2048
1826
|
}
|
|
2049
1827
|
}
|
|
2050
1828
|
}
|
|
2051
1829
|
}
|
|
2052
1830
|
}
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
if (
|
|
2061
|
-
|
|
2062
|
-
} else if (hasReadOnly && !hasWriteOnly) {
|
|
2063
|
-
responseSchemas.add(name);
|
|
1831
|
+
(0, import_openapi_core6.expandTransitiveReferences)(requestSchemas, this.spec);
|
|
1832
|
+
(0, import_openapi_core6.expandTransitiveReferences)(responseSchemas, this.spec);
|
|
1833
|
+
for (const [name] of Object.entries(((_a = this.spec.components) == null ? void 0 : _a.schemas) || {})) {
|
|
1834
|
+
if (requestSchemas.has(name) && responseSchemas.has(name)) {
|
|
1835
|
+
this.schemaUsageMap.set(name, "both");
|
|
1836
|
+
} else if (requestSchemas.has(name)) {
|
|
1837
|
+
this.schemaUsageMap.set(name, "request");
|
|
1838
|
+
} else if (responseSchemas.has(name)) {
|
|
1839
|
+
this.schemaUsageMap.set(name, "response");
|
|
2064
1840
|
}
|
|
2065
1841
|
}
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
if (requestSchemas.has(name) && responseSchemas.has(name)) {
|
|
1842
|
+
const circularSchemas = (0, import_openapi_core6.detectCircularReferences)(this.spec);
|
|
1843
|
+
for (const name of circularSchemas) {
|
|
2069
1844
|
this.schemaUsageMap.set(name, "both");
|
|
2070
|
-
} else if (requestSchemas.has(name)) {
|
|
2071
|
-
this.schemaUsageMap.set(name, "request");
|
|
2072
|
-
} else if (responseSchemas.has(name)) {
|
|
2073
|
-
this.schemaUsageMap.set(name, "response");
|
|
2074
|
-
}
|
|
2075
|
-
}
|
|
2076
|
-
this.detectCircularReferences();
|
|
2077
|
-
}
|
|
2078
|
-
/**
|
|
2079
|
-
* Expand a set of schemas to include all transitively referenced schemas
|
|
2080
|
-
*/
|
|
2081
|
-
expandTransitiveReferences(schemas) {
|
|
2082
|
-
var _a, _b;
|
|
2083
|
-
const toProcess = Array.from(schemas);
|
|
2084
|
-
const processed = /* @__PURE__ */ new Set();
|
|
2085
|
-
while (toProcess.length > 0) {
|
|
2086
|
-
const schemaName = toProcess.pop();
|
|
2087
|
-
if (!schemaName || processed.has(schemaName)) continue;
|
|
2088
|
-
processed.add(schemaName);
|
|
2089
|
-
const schema = (_b = (_a = this.spec.components) == null ? void 0 : _a.schemas) == null ? void 0 : _b[schemaName];
|
|
2090
|
-
if (schema) {
|
|
2091
|
-
const refs = /* @__PURE__ */ new Set();
|
|
2092
|
-
this.extractSchemaRefs(schema, refs);
|
|
2093
|
-
for (const ref of refs) {
|
|
2094
|
-
if (!schemas.has(ref)) {
|
|
2095
|
-
schemas.add(ref);
|
|
2096
|
-
toProcess.push(ref);
|
|
2097
|
-
}
|
|
2098
|
-
}
|
|
2099
|
-
}
|
|
2100
|
-
}
|
|
2101
|
-
}
|
|
2102
|
-
/**
|
|
2103
|
-
* Extract schema names from $ref and nested structures
|
|
2104
|
-
*/
|
|
2105
|
-
extractSchemaRefs(schema, refs) {
|
|
2106
|
-
if (!schema) return;
|
|
2107
|
-
if (schema.$ref) {
|
|
2108
|
-
const refName = resolveRef(schema.$ref);
|
|
2109
|
-
refs.add(refName);
|
|
2110
|
-
}
|
|
2111
|
-
if (schema.allOf) {
|
|
2112
|
-
for (const subSchema of schema.allOf) {
|
|
2113
|
-
this.extractSchemaRefs(subSchema, refs);
|
|
2114
|
-
}
|
|
2115
|
-
}
|
|
2116
|
-
if (schema.oneOf) {
|
|
2117
|
-
for (const subSchema of schema.oneOf) {
|
|
2118
|
-
this.extractSchemaRefs(subSchema, refs);
|
|
2119
|
-
}
|
|
2120
|
-
}
|
|
2121
|
-
if (schema.anyOf) {
|
|
2122
|
-
for (const subSchema of schema.anyOf) {
|
|
2123
|
-
this.extractSchemaRefs(subSchema, refs);
|
|
2124
|
-
}
|
|
2125
|
-
}
|
|
2126
|
-
if (schema.items) {
|
|
2127
|
-
this.extractSchemaRefs(schema.items, refs);
|
|
2128
|
-
}
|
|
2129
|
-
if (schema.properties) {
|
|
2130
|
-
for (const prop of Object.values(schema.properties)) {
|
|
2131
|
-
this.extractSchemaRefs(prop, refs);
|
|
2132
1845
|
}
|
|
2133
|
-
}
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
return false;
|
|
2146
|
-
}
|
|
2147
|
-
/**
|
|
2148
|
-
* Check if schema has writeOnly properties
|
|
2149
|
-
*/
|
|
2150
|
-
hasWriteOnlyProperties(schema) {
|
|
2151
|
-
if (schema.writeOnly) return true;
|
|
2152
|
-
if (schema.properties) {
|
|
2153
|
-
for (const prop of Object.values(schema.properties)) {
|
|
2154
|
-
if (this.hasWriteOnlyProperties(prop)) return true;
|
|
2155
|
-
}
|
|
2156
|
-
}
|
|
2157
|
-
return false;
|
|
2158
|
-
}
|
|
2159
|
-
/**
|
|
2160
|
-
* Detect circular references and mark them as "both" context for safety
|
|
2161
|
-
*/
|
|
2162
|
-
detectCircularReferences() {
|
|
2163
|
-
var _a;
|
|
2164
|
-
const visited = /* @__PURE__ */ new Set();
|
|
2165
|
-
const recursionStack = /* @__PURE__ */ new Set();
|
|
2166
|
-
const detectCycle = (name) => {
|
|
2167
|
-
var _a2, _b;
|
|
2168
|
-
if (recursionStack.has(name)) {
|
|
2169
|
-
return true;
|
|
2170
|
-
}
|
|
2171
|
-
if (visited.has(name)) {
|
|
2172
|
-
return false;
|
|
2173
|
-
}
|
|
2174
|
-
visited.add(name);
|
|
2175
|
-
recursionStack.add(name);
|
|
2176
|
-
const schema = (_b = (_a2 = this.spec.components) == null ? void 0 : _a2.schemas) == null ? void 0 : _b[name];
|
|
2177
|
-
if (schema) {
|
|
2178
|
-
const refs = /* @__PURE__ */ new Set();
|
|
2179
|
-
this.extractSchemaRefs(schema, refs);
|
|
2180
|
-
for (const ref of refs) {
|
|
2181
|
-
if (detectCycle(ref)) {
|
|
2182
|
-
this.schemaUsageMap.set(name, "both");
|
|
2183
|
-
recursionStack.delete(name);
|
|
2184
|
-
return true;
|
|
1846
|
+
} else {
|
|
1847
|
+
const analysis = (0, import_openapi_core6.analyzeSchemaUsage)(this.spec);
|
|
1848
|
+
this.schemaUsageMap = analysis.usageMap;
|
|
1849
|
+
if (this.spec.paths) {
|
|
1850
|
+
for (const pathItem of Object.values(this.spec.paths)) {
|
|
1851
|
+
if (!isOpenAPIPathItem(pathItem)) continue;
|
|
1852
|
+
for (const method of HTTP_METHODS) {
|
|
1853
|
+
const operation = pathItem[method];
|
|
1854
|
+
if (typeof operation === "object" && operation) {
|
|
1855
|
+
this.filterStats.totalOperations++;
|
|
1856
|
+
this.filterStats.includedOperations++;
|
|
1857
|
+
}
|
|
2185
1858
|
}
|
|
2186
1859
|
}
|
|
2187
1860
|
}
|
|
2188
|
-
recursionStack.delete(name);
|
|
2189
|
-
return false;
|
|
2190
|
-
};
|
|
2191
|
-
for (const name of Object.keys(((_a = this.spec.components) == null ? void 0 : _a.schemas) || {})) {
|
|
2192
|
-
detectCycle(name);
|
|
2193
1861
|
}
|
|
2194
1862
|
}
|
|
2195
1863
|
/**
|
|
@@ -2198,7 +1866,7 @@ var OpenApiGenerator = class {
|
|
|
2198
1866
|
validateSpec() {
|
|
2199
1867
|
var _a;
|
|
2200
1868
|
if (!((_a = this.spec.components) == null ? void 0 : _a.schemas)) {
|
|
2201
|
-
throw new SpecValidationError(
|
|
1869
|
+
throw new import_openapi_core6.SpecValidationError(
|
|
2202
1870
|
`No schemas found in OpenAPI spec at ${this.options.input}. Expected to find schemas at components.schemas`,
|
|
2203
1871
|
{ filePath: this.options.input }
|
|
2204
1872
|
);
|
|
@@ -2209,7 +1877,7 @@ var OpenApiGenerator = class {
|
|
|
2209
1877
|
this.validateSchemaRefs(name, schema, allSchemas);
|
|
2210
1878
|
} catch (error) {
|
|
2211
1879
|
if (error instanceof Error) {
|
|
2212
|
-
throw new SchemaGenerationError(`Invalid schema '${name}': ${error.message}`, name, {
|
|
1880
|
+
throw new import_openapi_core6.SchemaGenerationError(`Invalid schema '${name}': ${error.message}`, name, {
|
|
2213
1881
|
originalError: error.message
|
|
2214
1882
|
});
|
|
2215
1883
|
}
|
|
@@ -2222,9 +1890,9 @@ var OpenApiGenerator = class {
|
|
|
2222
1890
|
*/
|
|
2223
1891
|
validateSchemaRefs(schemaName, schema, allSchemas, path = "") {
|
|
2224
1892
|
if (schema.$ref) {
|
|
2225
|
-
const refName =
|
|
1893
|
+
const refName = (0, import_openapi_core6.resolveRefName)(schema.$ref);
|
|
2226
1894
|
if (!allSchemas.includes(refName)) {
|
|
2227
|
-
throw new SpecValidationError(
|
|
1895
|
+
throw new import_openapi_core6.SpecValidationError(
|
|
2228
1896
|
`Invalid reference${path ? ` at '${path}'` : ""}: '${schema.$ref}' points to non-existent schema '${refName}'`,
|
|
2229
1897
|
{ schemaName, path, ref: schema.$ref, refName }
|
|
2230
1898
|
);
|
|
@@ -2271,7 +1939,7 @@ var OpenApiGenerator = class {
|
|
|
2271
1939
|
const resolvedOptions = context === "response" ? this.responseOptions : this.requestOptions;
|
|
2272
1940
|
if (schema.enum) {
|
|
2273
1941
|
const jsdoc2 = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
|
|
2274
|
-
const strippedName2 = stripPrefix(name, this.options.stripSchemaPrefix);
|
|
1942
|
+
const strippedName2 = (0, import_openapi_core6.stripPrefix)(name, this.options.stripSchemaPrefix);
|
|
2275
1943
|
const { schemaCode, typeCode } = generateEnum(strippedName2, schema.enum, {
|
|
2276
1944
|
prefix: this.options.prefix,
|
|
2277
1945
|
suffix: this.options.suffix
|
|
@@ -2281,11 +1949,11 @@ ${typeCode}`;
|
|
|
2281
1949
|
this.schemas.set(name, enumSchemaCode);
|
|
2282
1950
|
return;
|
|
2283
1951
|
}
|
|
2284
|
-
const strippedName = stripPrefix(name, this.options.stripSchemaPrefix);
|
|
2285
|
-
const schemaName = `${toCamelCase(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
|
|
2286
|
-
|
|
1952
|
+
const strippedName = (0, import_openapi_core6.stripPrefix)(name, this.options.stripSchemaPrefix);
|
|
1953
|
+
const schemaName = `${(0, import_openapi_core6.toCamelCase)(strippedName, { prefix: this.options.prefix, suffix: this.options.suffix })}Schema`;
|
|
1954
|
+
let jsdoc = generateJSDoc(schema, name, { includeDescriptions: resolvedOptions.includeDescriptions });
|
|
2287
1955
|
if (schema.allOf && schema.allOf.length === 1 && schema.allOf[0].$ref) {
|
|
2288
|
-
const refName =
|
|
1956
|
+
const refName = (0, import_openapi_core6.resolveRefName)(schema.allOf[0].$ref);
|
|
2289
1957
|
(_a = this.schemaDependencies.get(name)) == null ? void 0 : _a.add(refName);
|
|
2290
1958
|
}
|
|
2291
1959
|
this.propertyGenerator = new PropertyGenerator({
|
|
@@ -2301,9 +1969,28 @@ ${typeCode}`;
|
|
|
2301
1969
|
prefix: this.options.prefix,
|
|
2302
1970
|
suffix: this.options.suffix
|
|
2303
1971
|
},
|
|
2304
|
-
stripSchemaPrefix: this.options.stripSchemaPrefix
|
|
1972
|
+
stripSchemaPrefix: this.options.stripSchemaPrefix,
|
|
1973
|
+
dateTimeValidation: this.dateTimeValidation,
|
|
1974
|
+
patternCache: this.patternCache,
|
|
1975
|
+
separateTypesFile: this.separateSchemasMode
|
|
2305
1976
|
});
|
|
1977
|
+
this.propertyGenerator.setCircularDependencies(this.circularDependencies);
|
|
1978
|
+
this.propertyGenerator.clearAllOfConflicts();
|
|
2306
1979
|
const zodSchema = this.propertyGenerator.generatePropertySchema(schema, name, true);
|
|
1980
|
+
const allOfConflicts = this.propertyGenerator.getAllOfConflicts();
|
|
1981
|
+
if (allOfConflicts.length > 0) {
|
|
1982
|
+
this.allOfConflictCount += allOfConflicts.length;
|
|
1983
|
+
const conflictWarning = this.generateConflictJSDoc(allOfConflicts);
|
|
1984
|
+
if (jsdoc) {
|
|
1985
|
+
jsdoc = jsdoc.replace(/ \*\/\n$/, `
|
|
1986
|
+
${conflictWarning} */
|
|
1987
|
+
`);
|
|
1988
|
+
} else {
|
|
1989
|
+
jsdoc = `/**
|
|
1990
|
+
${conflictWarning} */
|
|
1991
|
+
`;
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
2307
1994
|
const zodSchemaCode = `${jsdoc}export const ${schemaName} = ${zodSchema};`;
|
|
2308
1995
|
if (zodSchema.includes("z.discriminatedUnion(")) {
|
|
2309
1996
|
const match = zodSchema.match(/z\.discriminatedUnion\([^,]+,\s*\[([^\]]+)\]/);
|
|
@@ -2329,28 +2016,27 @@ ${typeCode}`;
|
|
|
2329
2016
|
return;
|
|
2330
2017
|
}
|
|
2331
2018
|
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
|
|
2332
|
-
if (!pathItem
|
|
2333
|
-
const
|
|
2334
|
-
for (const method of methods) {
|
|
2019
|
+
if (!isOpenAPIPathItem(pathItem)) continue;
|
|
2020
|
+
for (const method of HTTP_METHODS) {
|
|
2335
2021
|
const operation = pathItem[method];
|
|
2336
2022
|
if (!operation) continue;
|
|
2337
|
-
if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
|
|
2023
|
+
if (!(0, import_openapi_core6.shouldIncludeOperation)(operation, path, method, this.options.operationFilters)) {
|
|
2338
2024
|
continue;
|
|
2339
2025
|
}
|
|
2340
|
-
const allParams = mergeParameters(pathItem.parameters, operation.parameters, this.spec);
|
|
2026
|
+
const allParams = (0, import_openapi_core6.mergeParameters)(pathItem.parameters, operation.parameters, this.spec);
|
|
2341
2027
|
const queryParams = allParams.filter(
|
|
2342
|
-
(param) => param &&
|
|
2028
|
+
(param) => isResolvedParameter(param) && param.in === "query"
|
|
2343
2029
|
);
|
|
2344
2030
|
if (queryParams.length === 0) {
|
|
2345
2031
|
continue;
|
|
2346
2032
|
}
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2033
|
+
const strippedPath = (0, import_openapi_core6.stripPathPrefix)(path, this.options.stripPathPrefix);
|
|
2034
|
+
const pascalOperationId = (0, import_openapi_core6.getOperationName)(
|
|
2035
|
+
operation.operationId,
|
|
2036
|
+
method,
|
|
2037
|
+
strippedPath,
|
|
2038
|
+
this.options.useOperationId
|
|
2039
|
+
);
|
|
2354
2040
|
const schemaName = `${pascalOperationId}QueryParams`;
|
|
2355
2041
|
if (!this.schemaDependencies.has(schemaName)) {
|
|
2356
2042
|
this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
|
|
@@ -2380,7 +2066,7 @@ ${typeCode}`;
|
|
|
2380
2066
|
required.push(paramName);
|
|
2381
2067
|
}
|
|
2382
2068
|
if (paramSchema.$ref) {
|
|
2383
|
-
const refName =
|
|
2069
|
+
const refName = (0, import_openapi_core6.resolveRefName)(paramSchema.$ref);
|
|
2384
2070
|
(_a = this.schemaDependencies.get(schemaName)) == null ? void 0 : _a.add(refName);
|
|
2385
2071
|
}
|
|
2386
2072
|
}
|
|
@@ -2395,9 +2081,7 @@ ${typeCode}`;
|
|
|
2395
2081
|
${propsCode}
|
|
2396
2082
|
})`;
|
|
2397
2083
|
const operationName = pascalOperationId;
|
|
2398
|
-
const
|
|
2399
|
-
const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
|
|
2400
|
-
const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}QueryParamsSchema`;
|
|
2084
|
+
const camelCaseSchemaName = `${(0, import_openapi_core6.toCamelCase)(operationName, { prefix: this.options.prefix, suffix: this.options.suffix })}QueryParamsSchema`;
|
|
2401
2085
|
const jsdocOperationName = operation.operationId || `${method.toUpperCase()} ${path}`;
|
|
2402
2086
|
const jsdoc = `/**
|
|
2403
2087
|
* Query parameters for ${jsdocOperationName}
|
|
@@ -2409,35 +2093,6 @@ ${propsCode}
|
|
|
2409
2093
|
}
|
|
2410
2094
|
}
|
|
2411
2095
|
}
|
|
2412
|
-
/**
|
|
2413
|
-
* Generate a PascalCase method name from HTTP method and path
|
|
2414
|
-
* Used as fallback when operationId is not available
|
|
2415
|
-
* @internal
|
|
2416
|
-
*/
|
|
2417
|
-
generateMethodNameFromPath(method, path) {
|
|
2418
|
-
const segments = path.split("/").filter(Boolean).map((segment) => {
|
|
2419
|
-
if (segment.startsWith("{") && segment.endsWith("}")) {
|
|
2420
|
-
const paramName = segment.slice(1, -1);
|
|
2421
|
-
return `By${this.capitalizeSegment(paramName)}`;
|
|
2422
|
-
}
|
|
2423
|
-
return this.capitalizeSegment(segment);
|
|
2424
|
-
}).join("");
|
|
2425
|
-
const capitalizedMethod = method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();
|
|
2426
|
-
return `${capitalizedMethod}${segments}`;
|
|
2427
|
-
}
|
|
2428
|
-
/**
|
|
2429
|
-
* Capitalizes a path segment, handling special characters like dashes, underscores, and dots
|
|
2430
|
-
* @internal
|
|
2431
|
-
*/
|
|
2432
|
-
capitalizeSegment(str) {
|
|
2433
|
-
if (str.includes("-") || str.includes("_") || str.includes(".")) {
|
|
2434
|
-
return str.split(/[-_.]/).map((part) => {
|
|
2435
|
-
if (!part) return "";
|
|
2436
|
-
return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
|
|
2437
|
-
}).join("");
|
|
2438
|
-
}
|
|
2439
|
-
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
2440
|
-
}
|
|
2441
2096
|
/**
|
|
2442
2097
|
* Check if a header should be ignored based on filter patterns
|
|
2443
2098
|
* @internal
|
|
@@ -2453,7 +2108,7 @@ ${propsCode}
|
|
|
2453
2108
|
const headerLower = headerName.toLowerCase();
|
|
2454
2109
|
return ignorePatterns.some((pattern) => {
|
|
2455
2110
|
const patternLower = pattern.toLowerCase();
|
|
2456
|
-
return (0,
|
|
2111
|
+
return (0, import_minimatch.minimatch)(headerLower, patternLower);
|
|
2457
2112
|
});
|
|
2458
2113
|
}
|
|
2459
2114
|
/**
|
|
@@ -2466,28 +2121,27 @@ ${propsCode}
|
|
|
2466
2121
|
return;
|
|
2467
2122
|
}
|
|
2468
2123
|
for (const [path, pathItem] of Object.entries(this.spec.paths)) {
|
|
2469
|
-
if (!pathItem
|
|
2470
|
-
const
|
|
2471
|
-
for (const method of methods) {
|
|
2124
|
+
if (!isOpenAPIPathItem(pathItem)) continue;
|
|
2125
|
+
for (const method of HTTP_METHODS) {
|
|
2472
2126
|
const operation = pathItem[method];
|
|
2473
2127
|
if (!operation) continue;
|
|
2474
|
-
if (!shouldIncludeOperation(operation, path, method, this.options.operationFilters)) {
|
|
2128
|
+
if (!(0, import_openapi_core6.shouldIncludeOperation)(operation, path, method, this.options.operationFilters)) {
|
|
2475
2129
|
continue;
|
|
2476
2130
|
}
|
|
2477
|
-
const allParams = mergeParameters(pathItem.parameters, operation.parameters, this.spec);
|
|
2131
|
+
const allParams = (0, import_openapi_core6.mergeParameters)(pathItem.parameters, operation.parameters, this.spec);
|
|
2478
2132
|
const headerParams = allParams.filter(
|
|
2479
|
-
(param) => param &&
|
|
2133
|
+
(param) => isResolvedParameter(param) && param.in === "header" && !this.shouldIgnoreHeader(param.name)
|
|
2480
2134
|
);
|
|
2481
2135
|
if (headerParams.length === 0) {
|
|
2482
2136
|
continue;
|
|
2483
2137
|
}
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2138
|
+
const strippedPath = (0, import_openapi_core6.stripPathPrefix)(path, this.options.stripPathPrefix);
|
|
2139
|
+
const pascalOperationId = (0, import_openapi_core6.getOperationName)(
|
|
2140
|
+
operation.operationId,
|
|
2141
|
+
method,
|
|
2142
|
+
strippedPath,
|
|
2143
|
+
this.options.useOperationId
|
|
2144
|
+
);
|
|
2491
2145
|
const schemaName = `${pascalOperationId}HeaderParams`;
|
|
2492
2146
|
if (!this.schemaDependencies.has(schemaName)) {
|
|
2493
2147
|
this.schemaDependencies.set(schemaName, /* @__PURE__ */ new Set());
|
|
@@ -2506,7 +2160,7 @@ ${propsCode}
|
|
|
2506
2160
|
zodType = `${zodType}.optional()`;
|
|
2507
2161
|
properties[paramName] = zodType;
|
|
2508
2162
|
if (paramSchema.$ref) {
|
|
2509
|
-
const refName =
|
|
2163
|
+
const refName = (0, import_openapi_core6.resolveRefName)(paramSchema.$ref);
|
|
2510
2164
|
(_a = this.schemaDependencies.get(schemaName)) == null ? void 0 : _a.add(refName);
|
|
2511
2165
|
}
|
|
2512
2166
|
}
|
|
@@ -2521,9 +2175,7 @@ ${propsCode}
|
|
|
2521
2175
|
${propsCode}
|
|
2522
2176
|
})`;
|
|
2523
2177
|
const operationName = pascalOperationId;
|
|
2524
|
-
const
|
|
2525
|
-
const suffixedName = this.options.suffix ? `${prefixedName}${toPascalCase(this.options.suffix)}` : prefixedName;
|
|
2526
|
-
const camelCaseSchemaName = `${suffixedName.charAt(0).toLowerCase() + suffixedName.slice(1)}HeaderParamsSchema`;
|
|
2178
|
+
const camelCaseSchemaName = `${(0, import_openapi_core6.toCamelCase)(operationName, { prefix: this.options.prefix, suffix: this.options.suffix })}HeaderParamsSchema`;
|
|
2527
2179
|
const jsdocOperationName = operation.operationId || `${method.toUpperCase()} ${path}`;
|
|
2528
2180
|
const jsdoc = `/**
|
|
2529
2181
|
* Header parameters for ${jsdocOperationName}
|
|
@@ -2540,9 +2192,9 @@ ${propsCode}
|
|
|
2540
2192
|
*/
|
|
2541
2193
|
generateQueryParamType(schema, param) {
|
|
2542
2194
|
if (schema.$ref) {
|
|
2543
|
-
const refName =
|
|
2544
|
-
const strippedRefName = stripPrefix(refName, this.options.stripSchemaPrefix);
|
|
2545
|
-
const schemaName = toCamelCase(strippedRefName, { prefix: this.options.prefix, suffix: this.options.suffix });
|
|
2195
|
+
const refName = (0, import_openapi_core6.resolveRefName)(schema.$ref);
|
|
2196
|
+
const strippedRefName = (0, import_openapi_core6.stripPrefix)(refName, this.options.stripSchemaPrefix);
|
|
2197
|
+
const schemaName = (0, import_openapi_core6.toCamelCase)(strippedRefName, { prefix: this.options.prefix, suffix: this.options.suffix });
|
|
2546
2198
|
return `${schemaName}Schema`;
|
|
2547
2199
|
}
|
|
2548
2200
|
if (schema.enum) {
|
|
@@ -2559,7 +2211,7 @@ ${propsCode}
|
|
|
2559
2211
|
if (typeof v === "string") {
|
|
2560
2212
|
return `z.literal("${v}")`;
|
|
2561
2213
|
}
|
|
2562
|
-
return `z.literal(${v})`;
|
|
2214
|
+
return `z.literal(${String(v)})`;
|
|
2563
2215
|
}).join(", ");
|
|
2564
2216
|
return `z.union([${literalValues}])`;
|
|
2565
2217
|
}
|
|
@@ -2639,17 +2291,23 @@ ${propsCode}
|
|
|
2639
2291
|
return;
|
|
2640
2292
|
}
|
|
2641
2293
|
const deps = this.schemaDependencies.get(name);
|
|
2294
|
+
let dependsOnCircular = false;
|
|
2642
2295
|
if (deps && deps.size > 0) {
|
|
2643
2296
|
for (const dep of deps) {
|
|
2644
2297
|
if (this.schemas.has(dep) || this.types.has(dep)) {
|
|
2645
2298
|
visit(dep);
|
|
2299
|
+
if (circularDeps.has(dep)) {
|
|
2300
|
+
dependsOnCircular = true;
|
|
2301
|
+
}
|
|
2646
2302
|
}
|
|
2647
2303
|
}
|
|
2648
2304
|
}
|
|
2649
2305
|
visiting.delete(name);
|
|
2650
2306
|
visited.add(name);
|
|
2651
|
-
if (!circularDeps.has(name)) {
|
|
2307
|
+
if (!circularDeps.has(name) && !dependsOnCircular) {
|
|
2652
2308
|
sorted.push(name);
|
|
2309
|
+
} else if (dependsOnCircular && !circularDeps.has(name)) {
|
|
2310
|
+
circularDeps.add(name);
|
|
2653
2311
|
}
|
|
2654
2312
|
};
|
|
2655
2313
|
const allNames = /* @__PURE__ */ new Set([...this.schemas.keys(), ...this.types.keys()]);
|
|
@@ -2657,9 +2315,8 @@ ${propsCode}
|
|
|
2657
2315
|
visit(name);
|
|
2658
2316
|
}
|
|
2659
2317
|
for (const name of circularDeps) {
|
|
2660
|
-
if (!
|
|
2318
|
+
if (!sorted.includes(name)) {
|
|
2661
2319
|
sorted.push(name);
|
|
2662
|
-
visited.add(name);
|
|
2663
2320
|
}
|
|
2664
2321
|
}
|
|
2665
2322
|
return [...sorted, ...aliases];
|
|
@@ -2686,11 +2343,12 @@ ${propsCode}
|
|
|
2686
2343
|
`// Total schemas: ${stats.totalSchemas}`,
|
|
2687
2344
|
`// Circular references: ${stats.withCircularRefs}`,
|
|
2688
2345
|
`// Discriminated unions: ${stats.withDiscriminators}`,
|
|
2689
|
-
`// With constraints: ${stats.withConstraints}
|
|
2346
|
+
`// With constraints: ${stats.withConstraints}`,
|
|
2347
|
+
`// AllOf conflicts: ${this.allOfConflictCount}`
|
|
2690
2348
|
];
|
|
2691
2349
|
if (this.options.operationFilters && this.filterStats.totalOperations > 0) {
|
|
2692
2350
|
output.push("//");
|
|
2693
|
-
const filterStatsStr = formatFilterStatistics(this.filterStats);
|
|
2351
|
+
const filterStatsStr = (0, import_openapi_core6.formatFilterStatistics)(this.filterStats);
|
|
2694
2352
|
for (const line of filterStatsStr.split("\n")) {
|
|
2695
2353
|
output.push(`// ${line}`);
|
|
2696
2354
|
}
|
|
@@ -2698,6 +2356,106 @@ ${propsCode}
|
|
|
2698
2356
|
output.push(`// Generated at: ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
2699
2357
|
return output;
|
|
2700
2358
|
}
|
|
2359
|
+
/**
|
|
2360
|
+
* Pre-analyze schemas to detect circular dependencies before code generation.
|
|
2361
|
+
* This allows the property generator to use z.lazy() for forward references.
|
|
2362
|
+
*/
|
|
2363
|
+
analyzeCircularDependencies() {
|
|
2364
|
+
var _a;
|
|
2365
|
+
if (!((_a = this.spec.components) == null ? void 0 : _a.schemas)) return;
|
|
2366
|
+
const dependencies = /* @__PURE__ */ new Map();
|
|
2367
|
+
const collectDependencies = (name, schema, visited2 = /* @__PURE__ */ new Set()) => {
|
|
2368
|
+
if (visited2.has(name)) return /* @__PURE__ */ new Set();
|
|
2369
|
+
visited2.add(name);
|
|
2370
|
+
const deps = /* @__PURE__ */ new Set();
|
|
2371
|
+
if (schema.$ref) {
|
|
2372
|
+
const refName = (0, import_openapi_core6.resolveRefName)(schema.$ref);
|
|
2373
|
+
deps.add(refName);
|
|
2374
|
+
}
|
|
2375
|
+
if (schema.allOf) {
|
|
2376
|
+
for (const item of schema.allOf) {
|
|
2377
|
+
const itemDeps = collectDependencies(`${name}_allOf`, item, new Set(visited2));
|
|
2378
|
+
for (const dep of itemDeps) deps.add(dep);
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
if (schema.oneOf) {
|
|
2382
|
+
for (const item of schema.oneOf) {
|
|
2383
|
+
const itemDeps = collectDependencies(`${name}_oneOf`, item, new Set(visited2));
|
|
2384
|
+
for (const dep of itemDeps) deps.add(dep);
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
if (schema.anyOf) {
|
|
2388
|
+
for (const item of schema.anyOf) {
|
|
2389
|
+
const itemDeps = collectDependencies(`${name}_anyOf`, item, new Set(visited2));
|
|
2390
|
+
for (const dep of itemDeps) deps.add(dep);
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
if (schema.properties) {
|
|
2394
|
+
for (const propSchema of Object.values(schema.properties)) {
|
|
2395
|
+
const propDeps = collectDependencies(`${name}_prop`, propSchema, new Set(visited2));
|
|
2396
|
+
for (const dep of propDeps) deps.add(dep);
|
|
2397
|
+
}
|
|
2398
|
+
}
|
|
2399
|
+
if (schema.items) {
|
|
2400
|
+
const itemDeps = collectDependencies(`${name}_items`, schema.items, new Set(visited2));
|
|
2401
|
+
for (const dep of itemDeps) deps.add(dep);
|
|
2402
|
+
}
|
|
2403
|
+
if (schema.additionalProperties && typeof schema.additionalProperties === "object") {
|
|
2404
|
+
const addDeps = collectDependencies(`${name}_additional`, schema.additionalProperties, new Set(visited2));
|
|
2405
|
+
for (const dep of addDeps) deps.add(dep);
|
|
2406
|
+
}
|
|
2407
|
+
return deps;
|
|
2408
|
+
};
|
|
2409
|
+
for (const [name, schema] of Object.entries(this.spec.components.schemas)) {
|
|
2410
|
+
if (this.options.operationFilters && this.schemaUsageMap.size > 0 && !this.schemaUsageMap.has(name)) {
|
|
2411
|
+
continue;
|
|
2412
|
+
}
|
|
2413
|
+
dependencies.set(name, collectDependencies(name, schema));
|
|
2414
|
+
}
|
|
2415
|
+
const visited = /* @__PURE__ */ new Set();
|
|
2416
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
2417
|
+
const detectCircular = (name, path = []) => {
|
|
2418
|
+
if (visited.has(name)) return;
|
|
2419
|
+
if (visiting.has(name)) {
|
|
2420
|
+
const cycleStart = path.indexOf(name);
|
|
2421
|
+
if (cycleStart >= 0) {
|
|
2422
|
+
for (let i = cycleStart; i < path.length; i++) {
|
|
2423
|
+
this.circularDependencies.add(path[i]);
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
this.circularDependencies.add(name);
|
|
2427
|
+
return;
|
|
2428
|
+
}
|
|
2429
|
+
visiting.add(name);
|
|
2430
|
+
path.push(name);
|
|
2431
|
+
const deps = dependencies.get(name);
|
|
2432
|
+
if (deps) {
|
|
2433
|
+
for (const dep of deps) {
|
|
2434
|
+
if (dependencies.has(dep)) {
|
|
2435
|
+
detectCircular(dep, [...path]);
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
visiting.delete(name);
|
|
2440
|
+
visited.add(name);
|
|
2441
|
+
};
|
|
2442
|
+
for (const name of dependencies.keys()) {
|
|
2443
|
+
detectCircular(name, []);
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
/**
|
|
2447
|
+
* Generate JSDoc warning for allOf conflicts
|
|
2448
|
+
* @param conflicts Array of conflict description strings
|
|
2449
|
+
* @returns JSDoc formatted warning string
|
|
2450
|
+
*/
|
|
2451
|
+
generateConflictJSDoc(conflicts) {
|
|
2452
|
+
const lines = [" * @warning allOf property conflicts detected:"];
|
|
2453
|
+
for (const conflict of conflicts) {
|
|
2454
|
+
lines.push(` * - ${conflict}`);
|
|
2455
|
+
}
|
|
2456
|
+
return `${lines.join("\n")}
|
|
2457
|
+
`;
|
|
2458
|
+
}
|
|
2701
2459
|
};
|
|
2702
2460
|
|
|
2703
2461
|
// src/types.ts
|
|
@@ -2712,8 +2470,10 @@ function defineConfig(config) {
|
|
|
2712
2470
|
FileOperationError,
|
|
2713
2471
|
GeneratorError,
|
|
2714
2472
|
OpenApiGenerator,
|
|
2473
|
+
PropertyGenerator,
|
|
2715
2474
|
SchemaGenerationError,
|
|
2716
2475
|
SpecValidationError,
|
|
2476
|
+
buildDateTimeValidation,
|
|
2717
2477
|
defineConfig
|
|
2718
2478
|
});
|
|
2719
2479
|
//# sourceMappingURL=index.js.map
|